From d66ad12f5c7a9d82e912fbd598745fc0e890d664 Mon Sep 17 00:00:00 2001 From: Dominik Pschenitschni Date: Sat, 11 Sep 2021 17:53:03 +0200 Subject: [PATCH] feat: improve kanban implementation --- package.json | 1 + src/main.ts | 6 +- src/services/bucket.js | 2 +- src/services/task.js | 3 +- src/store/index.js | 5 +- src/store/modules/kanban.js | 138 +++++++++++++++++++----- src/store/modules/tasks.js | 16 ++- src/views/list/views/Kanban.vue | 180 ++++++++++++++++---------------- src/views/list/views/List.vue | 2 +- src/views/list/views/Table.vue | 2 +- vite.config.js | 2 +- 11 files changed, 230 insertions(+), 127 deletions(-) diff --git a/package.json b/package.json index c08cb2ff1..4056edeb0 100644 --- a/package.json +++ b/package.json @@ -25,6 +25,7 @@ "easymde": "^2.15.0", "highlight.js": "11.2.0", "is-touch-device": "1.0.1", + "lodash.clonedeep": "^4.5.0", "marked": "3.0.4", "register-service-worker": "1.7.2", "snake-case": "3.0.4", diff --git a/src/main.ts b/src/main.ts index 76836c200..179446a0b 100644 --- a/src/main.ts +++ b/src/main.ts @@ -93,7 +93,11 @@ app.mixin({ }) app.config.errorHandler = (err, vm, info) => { - error(err) + // if (import.meta.env.PROD) { + // error(err) + // } else { + console.error(err, vm, info) + // } } app.config.globalProperties.$message = { diff --git a/src/services/bucket.js b/src/services/bucket.js index cabb78615..0e0c016ab 100644 --- a/src/services/bucket.js +++ b/src/services/bucket.js @@ -18,7 +18,7 @@ export default class BucketService extends AbstractService { beforeUpdate(model) { const taskService = new TaskService() - model.tasks = model.tasks.map(t => taskService.processModel(t)) + model.tasks = model.tasks?.map(t => taskService.processModel(t)) return model } } \ No newline at end of file diff --git a/src/services/task.js b/src/services/task.js index 1515ba938..361d08dd0 100644 --- a/src/services/task.js +++ b/src/services/task.js @@ -37,7 +37,8 @@ export default class TaskService extends AbstractService { return this.processModel(model) } - processModel(model) { + processModel(updatedModel) { + const model = { ...updatedModel } model.title = model.title?.trim() diff --git a/src/store/index.js b/src/store/index.js index 76768bea4..a1568f768 100644 --- a/src/store/index.js +++ b/src/store/index.js @@ -56,7 +56,10 @@ export const store = createStore({ state.errorMessage = error }, [ONLINE](state, online) { - state.online = import.meta.env.VITE_IS_ONLINE || online + if (import.meta.env.VITE_IS_ONLINE) { + console.log('Setting fake online state', import.meta.env.VITE_IS_ONLINE) + } + state.online = !!import.meta.env.VITE_IS_ONLINE || online }, [CURRENT_LIST](state, currentList) { diff --git a/src/store/modules/kanban.js b/src/store/modules/kanban.js index b46efa00d..b3de7e537 100644 --- a/src/store/modules/kanban.js +++ b/src/store/modules/kanban.js @@ -1,11 +1,16 @@ +import cloneDeep from 'lodash.clonedeep' + import {findById, findIndexById} from '@/helpers/utils' +import {i18n} from '@/i18n' +import {success} from '@/message' + import BucketService from '../../services/bucket' import {setLoading} from '../helper' import TaskCollectionService from '@/services/taskCollection' const TASKS_PER_BUCKET = 25 -function getTaskPosition(state, task) { +function getTaskIndices(state, task) { const bucketIndex = findIndexById(state.buckets, task.bucketId) if (!bucketIndex) { @@ -167,7 +172,7 @@ export default { return } - const { bucketIndex, taskIndex } = getTaskPosition(state, task) + const { bucketIndex, taskIndex } = getTaskIndices(state, task) state.buckets[bucketIndex].tasks.splice(taskIndex, 1) }, @@ -189,23 +194,21 @@ export default { getBucketById(state) { return (bucketId) => findById(state.buckets, bucketId) }, - getTaskById: state => id => { - for (const b in state.buckets) { - for (const t in state.buckets[b].tasks) { - if (state.buckets[b].tasks[t].id === id) { - return { - bucketIndex: b, - taskIndex: t, - task: state.buckets[b].tasks[t], - } - } + + getTaskById(state) { + return (id) => { + let taskIndex + const bucketIndex = state.buckets.findIndex(({ tasks }) => { + taskIndex = findIndexById(tasks, id) + return taskIndex !== undefined + }) + + return { + bucketIndex: taskIndex || null, + taskIndex: taskIndex || null, + task: state.buckets?.[bucketIndex].tasks?.[taskIndex] || null, } } - return { - bucketIndex: null, - taskIndex: null, - task: null, - } }, }, @@ -256,6 +259,15 @@ export default { params.sort_by = 'kanban_position' params.order_by = 'asc' + // const hasBucketFilter = Object.entries(params.filter_by).some(([key, value]) => { + // const condition = value === 'bucket_id' + // if (condition) { + // if (value !== bucketId) { + // params.filter_value[key] = bucketId + // } + // } + // return condition + // }) let hasBucketFilter = false for (const f in params.filter_by) { if (params.filter_by[f] === 'bucket_id') { @@ -293,6 +305,7 @@ export default { ctx.commit('setBucketLoading', {bucketId: bucketId, loading: false}) }) }, + createBucket(ctx, bucket) { const cancel = setLoading(ctx, 'kanban') @@ -309,6 +322,7 @@ export default { cancel() }) }, + deleteBucket(ctx, {bucket, params}) { const cancel = setLoading(ctx, 'kanban') @@ -327,24 +341,96 @@ export default { cancel() }) }, - updateBucket(ctx, bucket) { + + updateBucket(ctx, updatedBucketData) { const cancel = setLoading(ctx, 'kanban') + const bucketIndex = findIndexById(ctx.state.buckets, updatedBucketData.id) + const oldBucket = cloneDeep(ctx.state.buckets[bucketIndex]) + + const bucket = ctx.state.buckets[bucketIndex] + + const requestData = { + id: updatedBucketData.id, + listId: updatedBucketData.listId || oldBucket.listId, + title: oldBucket.title, // can't be empty in request + // ...bucket, + ...updatedBucketData, + } + + const updatedBucket = { + ...bucket, + ...requestData, + } + ctx.commit('setBucketByIndex', {bucketIndex, bucket: updatedBucket}) + const bucketService = new BucketService() - return bucketService.update(bucket) + return bucketService.update(updatedBucket) .then(r => { - const bi = findById(ctx.state.buckets, r.id) - const bucket = r - bucket.tasks = ctx.state.buckets[bi].tasks - ctx.commit('setBucketByIndex', {bucketIndex: bi, bucket}) - return Promise.resolve(r) + Promise.resolve(r) }) .catch(e => { + // restore original state + ctx.commit('setBucketByIndex', {bucketIndex, bucket: oldBucket}) + return Promise.reject(e) }) - .finally(() => { - cancel() + .finally(() => cancel()) + }, + + updateBuckets(ctx, updatedBucketsData) { + const cancel = setLoading(ctx, 'kanban') + + const oldBuckets = [] + const updatedBuckets = updatedBucketsData.map((updatedBucketData) => { + const bucketIndex = findIndexById(ctx.state.buckets, updatedBucketData.id) + const bucket = ctx.state.buckets[bucketIndex] + + const oldBucket = cloneDeep(bucket) + oldBuckets.push(oldBucket) + + const newBucket = { + // FIXME: maybe optional to set the original value as well + ...bucket, + id: updatedBucketData.id, + listId: updatedBucketData.listId || oldBucket.listId, + ...updatedBucketData, + } + ctx.commit('setBucketByIndex', {bucketIndex, bucket: newBucket}) + + const bucketService = new BucketService() + return bucketService.update(newBucket) + }) + + return Promise.all(updatedBuckets) + .then(r => { + Promise.resolve(r) }) + .catch(e => { + // restore original state + Object.values(updatedBuckets).forEach((oldBucket) => ctx.commit('setBucketById', oldBucket)) + + return Promise.reject(e) + }) + .finally(() => cancel()) + }, + + updateBucketTitle(ctx, { id, title }) { + const bucket = findById(ctx.state.buckets, id) + + if (bucket.title === title) { + // bucket title has not changed + return + } + + const updatedBucketData = { + id, + title, + } + + ctx.dispatch('updateBucket', updatedBucketData).then(() => { + success({message: i18n.global.t('list.kanban.bucketTitleSavedSuccess')}) + }) }, }, } \ No newline at end of file diff --git a/src/store/modules/tasks.js b/src/store/modules/tasks.js index cae20b9b5..cb5c66337 100644 --- a/src/store/modules/tasks.js +++ b/src/store/modules/tasks.js @@ -117,14 +117,24 @@ export default { addTaskAttachment(ctx, {taskId, attachment}) { const t = ctx.rootGetters['kanban/getTaskById'](taskId) if (t.task !== null) { - const newTask = { ...t } - newTask.task.attachments.push(attachment) + const attachments = [ + ...t.task.attachments, + attachment, + ] + + const newTask = { + ...t, + task: { + ...t.task, + attachments, + }, + } ctx.commit('kanban/setTaskInBucketByIndex', newTask, {root: true}) } ctx.commit('attachments/add', attachment, {root: true}) }, - addAssignee(ctx, {user, taskId}) { + addAssignee(ctx, {user, taskId}) { const taskAssignee = new TaskAssigneeModel({userId: user.id, taskId: taskId}) const taskAssigneeService = new TaskAssigneeService() diff --git a/src/views/list/views/Kanban.vue b/src/views/list/views/Kanban.vue index f5443c931..16abf8154 100644 --- a/src/views/list/views/Kanban.vue +++ b/src/views/list/views/Kanban.vue @@ -1,9 +1,9 @@