diff options
author | Lauri Ojansivu <x@xet7.org> | 2017-10-09 14:49:38 +0300 |
---|---|---|
committer | Lauri Ojansivu <x@xet7.org> | 2017-10-09 14:49:38 +0300 |
commit | fb060ed2c52ee80d9f25d9e8431280b092f74da0 (patch) | |
tree | 3e325e5f2d19b72811c596727ca67e02d7b47f4a | |
parent | c84187bdadb7f1afaa8211773d1d1cb986709099 (diff) | |
parent | f77da76c682ec7ad1b5e141d113d4b74371f46ae (diff) | |
download | wekan-fb060ed2c52ee80d9f25d9e8431280b092f74da0.tar.gz wekan-fb060ed2c52ee80d9f25d9e8431280b092f74da0.tar.bz2 wekan-fb060ed2c52ee80d9f25d9e8431280b092f74da0.zip |
Merge branch 'issue783' of https://github.com/amadilsons/wekan into amadilsons-issue783
-rw-r--r-- | client/components/lists/list.js | 2 | ||||
-rw-r--r-- | client/components/lists/list.styl | 16 | ||||
-rw-r--r-- | client/components/lists/listBody.jade | 2 | ||||
-rw-r--r-- | client/components/lists/listBody.js | 17 | ||||
-rw-r--r-- | client/components/lists/listHeader.jade | 26 | ||||
-rw-r--r-- | client/components/lists/listHeader.js | 52 | ||||
-rw-r--r-- | client/components/sidebar/sidebarArchives.js | 4 | ||||
-rw-r--r-- | client/lib/popup.js | 1 | ||||
-rw-r--r-- | i18n/en-GB.i18n.json | 9 | ||||
-rw-r--r-- | i18n/en.i18n.json | 7 | ||||
-rw-r--r-- | i18n/pt-BR.i18n.json | 9 | ||||
-rw-r--r-- | models/cards.js | 8 | ||||
-rw-r--r-- | models/lists.js | 62 |
13 files changed, 202 insertions, 13 deletions
diff --git a/client/components/lists/list.js b/client/components/lists/list.js index 9c191348..0e913207 100644 --- a/client/components/lists/list.js +++ b/client/components/lists/list.js @@ -22,7 +22,7 @@ BlazeComponent.extendComponent({ const itemsSelector = '.js-minicard:not(.placeholder, .js-card-composer)'; const $cards = this.$('.js-minicards'); $cards.sortable({ - connectWith: '.js-minicards', + connectWith: '.js-minicards:not(.js-list-full)', tolerance: 'pointer', appendTo: 'body', helper(evt, item) { diff --git a/client/components/lists/list.styl b/client/components/lists/list.styl index 5e20476e..f426b243 100644 --- a/client/components/lists/list.styl +++ b/client/components/lists/list.styl @@ -110,3 +110,19 @@ background: #fafafa color: #222 box-shadow: 0 1px 2px rgba(0,0,0,.2) + +#js-wip-limit-edit + padding-top: 2% + + p + margin-bottom: 0 + + input + display: inline-block + + .wip-limit-value + width: 20% + margin-right: 5% + + .wip-limit-error + display: none diff --git a/client/components/lists/listBody.jade b/client/components/lists/listBody.jade index 01aa7179..840fd801 100644 --- a/client/components/lists/listBody.jade +++ b/client/components/lists/listBody.jade @@ -1,6 +1,6 @@ template(name="listBody") .list-body.js-perfect-scrollbar - .minicards.clearfix.js-minicards + .minicards.clearfix.js-minicards(class="{{#if reachedWipLimit}}js-list-full{{/if}}") if cards.count +inlinedForm(autoclose=false position="top") +addCardForm(listId=_id position="top") diff --git a/client/components/lists/listBody.js b/client/components/lists/listBody.js index 724e805b..22ed9e57 100644 --- a/client/components/lists/listBody.js +++ b/client/components/lists/listBody.js @@ -96,6 +96,16 @@ BlazeComponent.extendComponent({ MultiSelection.toggle(this.currentData()._id); }, + canSeeAddCard() { + return !this.reachedWipLimit() && Meteor.user() && Meteor.user().isBoardMember() && !Meteor.user().isCommentOnly(); + }, + + reachedWipLimit() { + const list = Template.currentData(); + if( !list.getWipLimit() ) { return false; } + return list.getWipLimit('enabled') && list.getWipLimit('value') === list.cards().count(); + }, + events() { return [{ 'click .js-minicard': this.clickOnMiniCard, @@ -239,10 +249,3 @@ BlazeComponent.extendComponent({ }); }, }).register('addCardForm'); - - -Template.listBody.helpers({ - canSeeAddCard() { - return Meteor.user() && Meteor.user().isBoardMember() && !Meteor.user().isCommentOnly(); - }, -}); diff --git a/client/components/lists/listHeader.jade b/client/components/lists/listHeader.jade index 68336320..d5738dd9 100644 --- a/client/components/lists/listHeader.jade +++ b/client/components/lists/listHeader.jade @@ -6,6 +6,9 @@ template(name="listHeader") h2.list-header-name( class="{{#if currentUser.isBoardMember}}js-open-inlined-form is-editable{{/if}}") = title + if isWipLimitEnabled + span + | ({{cards.count}}/#{wipLimit.value}) if showCardsCountForList cards.count = cards.count span.lowercase @@ -33,6 +36,10 @@ template(name="listActionPopup") if cards.count li: a.js-select-cards {{_ 'list-select-cards'}} hr + if currentUser.isBoardAdmin + ul.pop-over-list + li: a.js-set-wip-limit {{#if isWipLimitEnabled }}{{_ 'edit-wip-limit'}}{{else}}{{_ 'setWipLimitPopup-title'}}{{/if}} + hr ul.pop-over-list li: a.js-close-list {{_ 'archive-list'}} hr @@ -64,3 +71,22 @@ template(name="listDeletePopup") unless archived p {{_ "list-delete-suggest-archive"}} button.js-confirm.negate.full(type="submit") {{_ 'delete'}} + +template(name="setWipLimitPopup") + #js-wip-limit-edit + lable {{_ 'set-wip-limit-value'}} + ul.pop-over-list + li: a.js-enable-wip-limit {{_ 'enable-wip-limit'}} + if isWipLimitEnabled + i.fa.fa-check + if isWipLimitEnabled + p + input.wip-limit-value(type="number" value="{{ wipLimitValue }}" min="1" max="99" onkeydown="return false") + input.wip-limit-apply(type="submit" value="{{_ 'apply'}}") + input.wip-limit-error + +template(name="wipLimitErrorPopup") + .wip-limit-invalid + p {{_ 'wipLimitErrorPopup-dialog-pt1'}} + p {{_ 'wipLimitErrorPopup-dialog-pt2'}} + button.full.js-back-view(type="submit") {{_ 'cancel'}} diff --git a/client/components/lists/listHeader.js b/client/components/lists/listHeader.js index 7fe42884..9974c788 100644 --- a/client/components/lists/listHeader.js +++ b/client/components/lists/listHeader.js @@ -13,6 +13,14 @@ BlazeComponent.extendComponent({ return list.findWatcher(Meteor.userId()); }, + isWipLimitEnabled() { + const wipLimit = this.currentData().getWipLimit(); + if(!wipLimit) { + return 0; + } + return wipLimit.enabled && wipLimit.value > 0; + }, + limitToShowCardsCount() { return Meteor.user().getLimitToShowCardsCount(); }, @@ -37,6 +45,10 @@ BlazeComponent.extendComponent({ }).register('listHeader'); Template.listActionPopup.helpers({ + isWipLimitEnabled() { + return Template.currentData().getWipLimit('enabled'); + }, + isWatching() { return this.findWatcher(Meteor.userId()); }, @@ -61,9 +73,49 @@ Template.listActionPopup.events({ this.archive(); Popup.close(); }, + 'click .js-set-wip-limit': Popup.open('setWipLimit'), 'click .js-more': Popup.open('listMore'), }); +BlazeComponent.extendComponent({ + applyWipLimit() { + const list = Template.currentData(); + const limit = parseInt(Template.instance().$('.wip-limit-value').val(), 10); + + if(limit < list.cards().count()){ + Template.instance().$('.wip-limit-error').click(); + } else { + Meteor.call('applyWipLimit', list._id, limit); + Popup.back(); + } + }, + + enableWipLimit() { + const list = Template.currentData(); + // Prevent user from using previously stored wipLimit.value if it is less than the current number of cards in the list + if(list.getWipLimit() && !list.getWipLimit('enabled') && list.getWipLimit('value') < list.cards().count()){ + list.setWipLimit(list.cards().count()); + } + Meteor.call('enableWipLimit', list._id); + }, + + isWipLimitEnabled() { + return Template.currentData().getWipLimit('enabled'); + }, + + wipLimitValue(){ + return Template.currentData().getWipLimit('value'); + }, + + events() { + return [{ + 'click .js-enable-wip-limit': this.enableWipLimit, + 'click .wip-limit-apply': this.applyWipLimit, + 'click .wip-limit-error': Popup.open('wipLimitError'), + }]; + }, +}).register('setWipLimitPopup'); + Template.listMorePopup.events({ 'click .js-delete': Popup.afterConfirm('listDelete', function () { Popup.close(); diff --git a/client/components/sidebar/sidebarArchives.js b/client/components/sidebar/sidebarArchives.js index c8196f23..2e8754b0 100644 --- a/client/components/sidebar/sidebarArchives.js +++ b/client/components/sidebar/sidebarArchives.js @@ -32,7 +32,9 @@ BlazeComponent.extendComponent({ return [{ 'click .js-restore-card'() { const card = this.currentData(); - card.restore(); + if(card.canBeRestored()){ + card.restore(); + } }, 'click .js-delete-card': Popup.afterConfirm('cardDelete', function() { const cardId = this._id; diff --git a/client/lib/popup.js b/client/lib/popup.js index 3658d883..d9e29ff1 100644 --- a/client/lib/popup.js +++ b/client/lib/popup.js @@ -205,4 +205,3 @@ escapeActions.forEach((actionName) => { } ); }); - diff --git a/i18n/en-GB.i18n.json b/i18n/en-GB.i18n.json index 2c18438d..718e487c 100644 --- a/i18n/en-GB.i18n.json +++ b/i18n/en-GB.i18n.json @@ -171,6 +171,7 @@ "edit": "Edit", "edit-avatar": "Change Avatar", "edit-profile": "Edit Profile", + "edit-wip-limit": "Edit WIP Limit", "editCardStartDatePopup-title": "Change start date", "editCardDueDatePopup-title": "Change due date", "editLabelPopup-title": "Change Label", @@ -189,6 +190,7 @@ "email-sent": "Email sent", "email-verifyEmail-subject": "Verify your email address on __siteName__", "email-verifyEmail-text": "Hello __user__,\n\nTo verify your account email, simply click the link below.\n\n__url__\n\nThanks.", + "enable-wip-limit": "Enable WIP Limit", "error-board-doesNotExist": "This board does not exist", "error-board-notAdmin": "You need to be admin of this board to do that", "error-board-notAMember": "You need to be a member of this board to do that", @@ -310,6 +312,8 @@ "save": "Save", "search": "Search", "select-color": "Select Color", + "set-wip-limit-value": "Set a limit for the maximum number of tasks in this list:", + "setWipLimitPopup-title": "Set WIP Limit", "shortcut-assign-self": "Assign yourself to current card", "shortcut-autocomplete-emoji": "Autocomplete emoji", "shortcut-autocomplete-members": "Autocomplete members", @@ -350,6 +354,9 @@ "welcome-list1": "Basics", "welcome-list2": "Advanced", "what-to-do": "What do you want to do?", + "wipLimitErrorPopup-title": "Invalid WIP Limit", + "wipLimitErrorPopup-dialog-pt1": "The number of tasks in this list is higher than the WIP limit you've defined.", + "wipLimitErrorPopup-dialog-pt2": "Please move some tasks out of this list, or set a higher WIP limit.", "admin-panel": "Admin Panel", "settings": "Settings", "people": "People", @@ -395,4 +402,4 @@ "no": "No", "accounts": "Accounts", "accounts-allowEmailChange": "Allow Email Change" -}
\ No newline at end of file +} diff --git a/i18n/en.i18n.json b/i18n/en.i18n.json index 95be9865..556910ff 100644 --- a/i18n/en.i18n.json +++ b/i18n/en.i18n.json @@ -171,6 +171,7 @@ "edit": "Edit", "edit-avatar": "Change Avatar", "edit-profile": "Edit Profile", + "edit-wip-limit": "Edit WIP Limit", "editCardStartDatePopup-title": "Change start date", "editCardDueDatePopup-title": "Change due date", "editLabelPopup-title": "Change Label", @@ -189,6 +190,7 @@ "email-sent": "Email sent", "email-verifyEmail-subject": "Verify your email address on __siteName__", "email-verifyEmail-text": "Hello __user__,\n\nTo verify your account email, simply click the link below.\n\n__url__\n\nThanks.", + "enable-wip-limit": "Enable WIP Limit", "error-board-doesNotExist": "This board does not exist", "error-board-notAdmin": "You need to be admin of this board to do that", "error-board-notAMember": "You need to be a member of this board to do that", @@ -310,6 +312,8 @@ "save": "Save", "search": "Search", "select-color": "Select Color", + "set-wip-limit-value": "Set a limit for the maximum number of tasks in this list", + "setWipLimitPopup-title": "Set WIP Limit", "shortcut-assign-self": "Assign yourself to current card", "shortcut-autocomplete-emoji": "Autocomplete emoji", "shortcut-autocomplete-members": "Autocomplete members", @@ -350,6 +354,9 @@ "welcome-list1": "Basics", "welcome-list2": "Advanced", "what-to-do": "What do you want to do?", + "wipLimitErrorPopup-title": "Invalid WIP Limit", + "wipLimitErrorPopup-dialog-pt1": "The number of tasks in this list is higher than the WIP limit you've defined.", + "wipLimitErrorPopup-dialog-pt2": "Please move some tasks out of this list, or set a higher WIP limit.", "admin-panel": "Admin Panel", "settings": "Settings", "people": "People", diff --git a/i18n/pt-BR.i18n.json b/i18n/pt-BR.i18n.json index ccb2bbe6..e219fb30 100644 --- a/i18n/pt-BR.i18n.json +++ b/i18n/pt-BR.i18n.json @@ -171,6 +171,7 @@ "edit": "Editar", "edit-avatar": "Alterar Avatar", "edit-profile": "Editar Perfil", + "edit-wip-limit": "Editar Limite WIP", "editCardStartDatePopup-title": "Altera data de início", "editCardDueDatePopup-title": "Altera data fim", "editLabelPopup-title": "Alterar Etiqueta", @@ -189,6 +190,7 @@ "email-sent": "Email enviado", "email-verifyEmail-subject": "Verifique seu endereço de email em __siteName__", "email-verifyEmail-text": "Olá __user__\nPara verificar sua conta de email, clique no link abaixo.\n__url__\nObrigado.", + "enable-wip-limit": "Ativar Limite WIP", "error-board-doesNotExist": "Este quadro não existe", "error-board-notAdmin": "Você precisa ser administrador desse quadro para fazer isto", "error-board-notAMember": "Você precisa ser um membro desse quadro para fazer isto", @@ -310,6 +312,8 @@ "save": "Salvar", "search": "Buscar", "select-color": "Selecionar Cor", + "set-wip-limit-value": "Defina um limite máximo para o número de tarefas nesta lista", + "setWipLimitPopup-title": "Definir Limite WIP", "shortcut-assign-self": "Atribuir a si o cartão atual", "shortcut-autocomplete-emoji": "Autocompletar emoji", "shortcut-autocomplete-members": "Preenchimento automático de membros", @@ -350,6 +354,9 @@ "welcome-list1": "Básico", "welcome-list2": "Avançado", "what-to-do": "O que você gostaria de fazer?", + "wipLimitErrorPopup-title": "Limite WIP Inválido", + "wipLimitErrorPopup-dialog-pt1": "O número de tarefas nesta lista excede o limite WIP definido.", + "wipLimitErrorPopup-dialog-pt2": "Por favor, mova algumas tarefas para fora desta lista, ou defina um limite WIP mais elevado.", "admin-panel": "Painel Administrativo", "settings": "Configurações", "people": "Pessoas", @@ -395,4 +402,4 @@ "no": "Não", "accounts": "Contas", "accounts-allowEmailChange": "Permitir Mudança de Email" -}
\ No newline at end of file +} diff --git a/models/cards.js b/models/cards.js index 0a440697..5b752ec3 100644 --- a/models/cards.js +++ b/models/cards.js @@ -179,6 +179,14 @@ Cards.helpers({ cardId: this._id, }); }, + + canBeRestored() { + const list = Lists.findOne({_id: this.listId}); + if(list.getWipLimit() && list.getWipLimit('enabled') && list.getWipLimit('value') === list.cards().count()){ + return false; + } + return true; + }, }); Cards.mutations({ diff --git a/models/lists.js b/models/lists.js index d9a5b8e2..1b999b07 100644 --- a/models/lists.js +++ b/models/lists.js @@ -42,6 +42,31 @@ Lists.attachSchema(new SimpleSchema({ } }, }, + wipLimit: { + type: Object, + optional: true, + }, + 'wipLimit.value': { + type: Number, + decimal: false, + autoValue() { + if(this.isInsert){ + return 0; + } + return this.value; + }, + optional: true, + }, + 'wipLimit.enabled':{ + type: Boolean, + autoValue() { + if(this.isInsert){ + return false; + } + return this.value; + }, + optional: true, + }, })); Lists.allow({ @@ -72,6 +97,17 @@ Lists.helpers({ board() { return Boards.findOne(this.boardId); }, + + getWipLimit(option){ + const list = Lists.findOne({ _id: this._id }); + if(!list.wipLimit) { // Necessary check to avoid exceptions for the case where the doc doesn't have the wipLimit field yet set + return 0; + } else if(!option) { + return list.wipLimit; + } else { + return list.wipLimit[option] ? list.wipLimit[option] : 0; // Necessary check to avoid exceptions for the case where the doc doesn't have the wipLimit field yet set + } + }, }); Lists.mutations({ @@ -86,6 +122,32 @@ Lists.mutations({ restore() { return { $set: { archived: false } }; }, + + toggleWipLimit(toggle) { + return { $set: { 'wipLimit.enabled': toggle } }; + }, + + setWipLimit(limit) { + return { $set: { 'wipLimit.value': limit } }; + }, +}); + +Meteor.methods({ + applyWipLimit(listId, limit){ + check(listId, String); + check(limit, Number); + Lists.findOne({ _id: listId }).setWipLimit(limit); + }, + + enableWipLimit(listId) { + check(listId, String); + const list = Lists.findOne({ _id: listId }); + if(list.getWipLimit()){ // Necessary check to avoid exceptions for the case where the doc doesn't have the wipLimit field yet set + list.toggleWipLimit(!list.getWipLimit('enabled')); + } else { + list.toggleWipLimit(true); // First time toggle is always to 'true' because default is 'false' + } + }, }); Lists.hookOptions.after.update = { fetchPrevious: false }; |