From cac8b0926332379e5c2c831c114e4bb493e16817 Mon Sep 17 00:00:00 2001 From: konrad Date: Fri, 4 Sep 2020 20:01:02 +0000 Subject: [PATCH] Add limits for kanban boards (#234) Prevent dropping a task onto a bucket which has its limit reached Fix closing the dropdown Add notice to show the limit Add input to change kanban bucket limit Add menu item to save bucket limit Fix parsing dates from the api Co-authored-by: kolaente Reviewed-on: https://kolaente.dev/vikunja/frontend/pulls/234 --- src/helpers/colorIsDark.js | 15 +++++---- src/main.js | 2 ++ src/models/bucket.js | 3 +- src/models/task.js | 2 +- src/services/attachment.js | 2 +- src/services/bucket.js | 9 +++++- src/services/label.js | 4 +-- src/services/linkShare.js | 4 +-- src/services/list.js | 4 +-- src/services/listUsers.js | 4 +-- src/services/namespace.js | 4 +-- src/services/task.js | 4 +-- src/services/taskAssignee.js | 2 +- src/services/taskCollection.js | 4 +-- src/services/taskComment.js | 4 +-- src/services/taskRelation.js | 2 +- src/services/team.js | 4 +-- src/services/teamList.js | 4 +-- src/services/teamMember.js | 4 +-- src/services/teamNamespace.js | 4 +-- src/services/user.js | 4 +-- src/services/userList.js | 4 +-- src/services/userNamespace.js | 4 +-- src/styles/components/kanban.scss | 17 ++++++++++ src/views/list/views/Kanban.vue | 53 ++++++++++++++++++++++++++++++- 25 files changed, 124 insertions(+), 43 deletions(-) diff --git a/src/helpers/colorIsDark.js b/src/helpers/colorIsDark.js index 4f7d2baca..01cf9e80c 100644 --- a/src/helpers/colorIsDark.js +++ b/src/helpers/colorIsDark.js @@ -1,15 +1,18 @@ - export const colorIsDark = color => { if (color === '#' || color === '') { return true // Defaults to dark } - let rgb = parseInt(color.substring(1, 7), 16); // convert rrggbb to decimal - let r = (rgb >> 16) & 0xff; // extract red - let g = (rgb >> 8) & 0xff; // extract green - let b = (rgb >> 0) & 0xff; // extract blue + if (color.substring(0, 1) !== '#') { + color = '#' + color + } + + let rgb = parseInt(color.substring(1, 7), 16) // convert rrggbb to decimal + let r = (rgb >> 16) & 0xff // extract red + let g = (rgb >> 8) & 0xff // extract green + let b = (rgb >> 0) & 0xff // extract blue // luma will be a value 0..255 where 0 indicates the darkest, and 255 the brightest - let luma = 0.2126 * r + 0.7152 * g + 0.0722 * b; // per ITU-R BT.709 + let luma = 0.2126 * r + 0.7152 * g + 0.0722 * b // per ITU-R BT.709 return luma > 128 } \ No newline at end of file diff --git a/src/main.js b/src/main.js index 3ac560569..5f6a2816c 100644 --- a/src/main.js +++ b/src/main.js @@ -70,6 +70,7 @@ import { faFilter } from '@fortawesome/free-solid-svg-icons' import { faFillDrip } from '@fortawesome/free-solid-svg-icons' import { faKeyboard } from '@fortawesome/free-solid-svg-icons' import { faComments } from '@fortawesome/free-regular-svg-icons' +import { faSave } from '@fortawesome/free-regular-svg-icons' import { FontAwesomeIcon } from '@fortawesome/vue-fontawesome' library.add(faSignOutAlt) @@ -117,6 +118,7 @@ library.add(faEllipsisV) library.add(faFilter) library.add(faFillDrip) library.add(faKeyboard) +library.add(faSave) Vue.component('icon', FontAwesomeIcon) diff --git a/src/models/bucket.js b/src/models/bucket.js index a874f0d87..16f4eebd1 100644 --- a/src/models/bucket.js +++ b/src/models/bucket.js @@ -1,6 +1,6 @@ import AbstractModel from './abstractModel' import UserModel from './user' -import TaskModel from "./task"; +import TaskModel from './task' export default class BucketModel extends AbstractModel { constructor(bucket) { @@ -18,6 +18,7 @@ export default class BucketModel extends AbstractModel { id: 0, title: '', listId: 0, + limit: 0, tasks: [], createdBy: null, diff --git a/src/models/task.js b/src/models/task.js index eec468886..c754fb649 100644 --- a/src/models/task.js +++ b/src/models/task.js @@ -75,7 +75,7 @@ export default class TaskModel extends AbstractModel { defaults() { return { id: 0, - text: '', + title: '', description: '', done: false, priority: 0, diff --git a/src/services/attachment.js b/src/services/attachment.js index fbdc4a97b..8c79f592c 100644 --- a/src/services/attachment.js +++ b/src/services/attachment.js @@ -12,7 +12,7 @@ export default class AttachmentService extends AbstractService { } processModel(model) { - model.created = formatISO(model.created) + model.created = formatISO(new Date(model.created)) return model } diff --git a/src/services/bucket.js b/src/services/bucket.js index 8c2daa666..cabb78615 100644 --- a/src/services/bucket.js +++ b/src/services/bucket.js @@ -1,5 +1,6 @@ import AbstractService from './abstractService' -import BucketModel from "../models/bucket"; +import BucketModel from '../models/bucket' +import TaskService from '@/services/task' export default class BucketService extends AbstractService { constructor() { @@ -14,4 +15,10 @@ export default class BucketService extends AbstractService { modelFactory(data) { return new BucketModel(data) } + + beforeUpdate(model) { + const taskService = new TaskService() + model.tasks = model.tasks.map(t => taskService.processModel(t)) + return model + } } \ No newline at end of file diff --git a/src/services/label.js b/src/services/label.js index 79b0d3be1..2a78c4d41 100644 --- a/src/services/label.js +++ b/src/services/label.js @@ -14,8 +14,8 @@ export default class LabelService extends AbstractService { } processModel(label) { - label.created = formatISO(label.created) - label.updated = formatISO(label.updated) + label.created = formatISO(new Date(label.created)) + label.updated = formatISO(new Date(label.updated)) label.hexColor = label.hexColor.substring(1, 7) return label } diff --git a/src/services/linkShare.js b/src/services/linkShare.js index 1bccfec9b..b5d6a55a3 100644 --- a/src/services/linkShare.js +++ b/src/services/linkShare.js @@ -13,8 +13,8 @@ export default class ListService extends AbstractService { } processModel(model) { - model.created = formatISO(model.created) - model.updated = formatISO(model.updated) + model.created = formatISO(new Date(model.created)) + model.updated = formatISO(new Date(model.updated)) return model } diff --git a/src/services/list.js b/src/services/list.js index a0a1a4297..9be8f729d 100644 --- a/src/services/list.js +++ b/src/services/list.js @@ -15,8 +15,8 @@ export default class ListService extends AbstractService { } processModel(model) { - model.created = formatISO(model.created) - model.updated = formatISO(model.updated) + model.created = formatISO(new Date(model.created)) + model.updated = formatISO(new Date(model.updated)) return model } diff --git a/src/services/listUsers.js b/src/services/listUsers.js index db0472c7f..3f262eecb 100644 --- a/src/services/listUsers.js +++ b/src/services/listUsers.js @@ -10,8 +10,8 @@ export default class ListUserService extends AbstractService { } processModel(model) { - model.created = formatISO(model.created) - model.updated = formatISO(model.updated) + model.created = formatISO(new Date(model.created)) + model.updated = formatISO(new Date(model.updated)) return model } diff --git a/src/services/namespace.js b/src/services/namespace.js index a23e3d809..3c78f326c 100644 --- a/src/services/namespace.js +++ b/src/services/namespace.js @@ -14,8 +14,8 @@ export default class NamespaceService extends AbstractService { } processModel(model) { - model.created = formatISO(model.created) - model.updated = formatISO(model.updated) + model.created = formatISO(new Date(model.created)) + model.updated = formatISO(new Date(model.updated)) return model } diff --git a/src/services/task.js b/src/services/task.js index 48269e242..5c1878578 100644 --- a/src/services/task.js +++ b/src/services/task.js @@ -37,8 +37,8 @@ export default class TaskService extends AbstractService { model.dueDate = !model.dueDate ? null : formatISO(new Date(model.dueDate)) model.startDate = !model.startDate ? null : formatISO(new Date(model.startDate)) model.endDate = !model.endDate ? null : formatISO(new Date(model.endDate)) - model.created = formatISO(model.created) - model.updated = formatISO(model.updated) + model.created = formatISO(new Date(model.created)) + model.updated = formatISO(new Date(model.updated)) // remove all nulls, these would create empty reminders for (const index in model.reminderDates) { diff --git a/src/services/taskAssignee.js b/src/services/taskAssignee.js index 022bce2ac..6b76960bd 100644 --- a/src/services/taskAssignee.js +++ b/src/services/taskAssignee.js @@ -11,7 +11,7 @@ export default class TaskAssigneeService extends AbstractService { } processModel(model) { - model.created = formatISO(model.created) + model.created = formatISO(new Date(model.created)) return model } diff --git a/src/services/taskCollection.js b/src/services/taskCollection.js index 333c234c9..51830862b 100644 --- a/src/services/taskCollection.js +++ b/src/services/taskCollection.js @@ -10,8 +10,8 @@ export default class TaskCollectionService extends AbstractService { } processModel(model) { - model.created = formatISO(model.created) - model.updated = formatISO(model.updated) + model.created = formatISO(new Date(model.created)) + model.updated = formatISO(new Date(model.updated)) return model } diff --git a/src/services/taskComment.js b/src/services/taskComment.js index fc7432729..2bf2375c0 100644 --- a/src/services/taskComment.js +++ b/src/services/taskComment.js @@ -14,8 +14,8 @@ export default class TaskCommentService extends AbstractService { } processModel(model) { - model.created = formatISO(model.created) - model.updated = formatISO(model.updated) + model.created = formatISO(new Date(model.created)) + model.updated = formatISO(new Date(model.updated)) return model } diff --git a/src/services/taskRelation.js b/src/services/taskRelation.js index a062ff930..ae80da005 100644 --- a/src/services/taskRelation.js +++ b/src/services/taskRelation.js @@ -11,7 +11,7 @@ export default class TaskRelationService extends AbstractService { } processModel(model) { - model.created = formatISO(model.created) + model.created = formatISO(new Date(model.created)) return model } diff --git a/src/services/team.js b/src/services/team.js index 685460d95..d1e379c1a 100644 --- a/src/services/team.js +++ b/src/services/team.js @@ -14,8 +14,8 @@ export default class TeamService extends AbstractService { } processModel(model) { - model.created = formatISO(model.created) - model.updated = formatISO(model.updated) + model.created = formatISO(new Date(model.created)) + model.updated = formatISO(new Date(model.updated)) return model } diff --git a/src/services/teamList.js b/src/services/teamList.js index 4f03c5b6c..d04af9274 100644 --- a/src/services/teamList.js +++ b/src/services/teamList.js @@ -14,8 +14,8 @@ export default class TeamListService extends AbstractService { } processModel(model) { - model.created = formatISO(model.created) - model.updated = formatISO(model.updated) + model.created = formatISO(new Date(model.created)) + model.updated = formatISO(new Date(model.updated)) return model } diff --git a/src/services/teamMember.js b/src/services/teamMember.js index 01cb0a39f..9927c82bd 100644 --- a/src/services/teamMember.js +++ b/src/services/teamMember.js @@ -12,8 +12,8 @@ export default class TeamMemberService extends AbstractService { } processModel(model) { - model.created = formatISO(model.created) - model.updated = formatISO(model.updated) + model.created = formatISO(new Date(model.created)) + model.updated = formatISO(new Date(model.updated)) return model } diff --git a/src/services/teamNamespace.js b/src/services/teamNamespace.js index 02e56308d..3ceb428ca 100644 --- a/src/services/teamNamespace.js +++ b/src/services/teamNamespace.js @@ -14,8 +14,8 @@ export default class TeamNamespaceService extends AbstractService { } processModel(model) { - model.created = formatISO(model.created) - model.updated = formatISO(model.updated) + model.created = formatISO(new Date(model.created)) + model.updated = formatISO(new Date(model.updated)) return model } diff --git a/src/services/user.js b/src/services/user.js index e65b547cf..74d67692d 100644 --- a/src/services/user.js +++ b/src/services/user.js @@ -10,8 +10,8 @@ export default class UserService extends AbstractService { } processModel(model) { - model.created = formatISO(model.created) - model.updated = formatISO(model.updated) + model.created = formatISO(new Date(model.created)) + model.updated = formatISO(new Date(model.updated)) return model } diff --git a/src/services/userList.js b/src/services/userList.js index 3f51ee39a..7cdcda592 100644 --- a/src/services/userList.js +++ b/src/services/userList.js @@ -14,8 +14,8 @@ export default class UserListService extends AbstractService { } processModel(model) { - model.created = formatISO(model.created) - model.updated = formatISO(model.updated) + model.created = formatISO(new Date(model.created)) + model.updated = formatISO(new Date(model.updated)) return model } diff --git a/src/services/userNamespace.js b/src/services/userNamespace.js index 7fea1d874..64473dff4 100644 --- a/src/services/userNamespace.js +++ b/src/services/userNamespace.js @@ -14,8 +14,8 @@ export default class UserNamespaceService extends AbstractService { } processModel(model) { - model.created = formatISO(model.created) - model.updated = formatISO(model.updated) + model.created = formatISO(new Date(model.created)) + model.updated = formatISO(new Date(model.updated)) return model } diff --git a/src/styles/components/kanban.scss b/src/styles/components/kanban.scss index 16e1db2f1..f246f8b49 100644 --- a/src/styles/components/kanban.scss +++ b/src/styles/components/kanban.scss @@ -213,6 +213,14 @@ $crazy-height-calculation: '100vh - 4.5rem - 1.5rem - 1em - 1.5em - 8px'; width: 100%; } } + + a.dropdown-item { + padding-right: 1rem; + + .input { + height: 2.25em; + } + } } .bucket-header { @@ -221,6 +229,15 @@ $crazy-height-calculation: '100vh - 4.5rem - 1.5rem - 1em - 1.5em - 8px'; justify-content: space-between; padding: .5em; + .limit { + padding-left: .5rem; + font-weight: bold; + + &.is-max { + color: $red; + } + } + .dropdown-trigger { cursor: pointer; } diff --git a/src/views/list/views/Kanban.vue b/src/views/list/views/Kanban.vue index 381d6be78..8e85e022d 100644 --- a/src/views/list/views/Kanban.vue +++ b/src/views/list/views/Kanban.vue @@ -9,6 +9,12 @@ @focusout="() => saveBucketTitle(bucket.id)" :ref="`bucket${bucket.id}title`" @keyup.ctrl.enter="() => saveBucketTitle(bucket.id)">{{ bucket.title }} + + {{ bucket.tasks.length }}/{{ bucket.limit }} +