mirror of
https://github.com/wekan/wekan.git
synced 2025-12-16 15:30:13 +01:00
Add checklist feature
This commit is contained in:
parent
38c143b8bf
commit
59731af139
15 changed files with 462 additions and 2 deletions
|
|
@ -117,6 +117,8 @@
|
||||||
"Notifications": true,
|
"Notifications": true,
|
||||||
"allowIsBoardAdmin": true,
|
"allowIsBoardAdmin": true,
|
||||||
"allowIsBoardMember": true,
|
"allowIsBoardMember": true,
|
||||||
"Emoji": true
|
"allowIsBoardMemberByCard": true,
|
||||||
|
"Emoji": true,
|
||||||
|
"Checklists": true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -26,6 +26,12 @@ template(name="boardActivities")
|
||||||
+viewer
|
+viewer
|
||||||
= comment.text
|
= comment.text
|
||||||
|
|
||||||
|
if($eq activityType 'addChecklist')
|
||||||
|
| {{{_ 'activity-checklist-added' cardLink}}}.
|
||||||
|
.activity-checklist(href="{{ card.absoluteUrl }}")
|
||||||
|
+viewer
|
||||||
|
= checklist.title
|
||||||
|
|
||||||
if($eq activityType 'archivedCard')
|
if($eq activityType 'archivedCard')
|
||||||
| {{{_ 'activity-archived' cardLink}}}.
|
| {{{_ 'activity-archived' cardLink}}}.
|
||||||
|
|
||||||
|
|
@ -103,6 +109,11 @@ template(name="cardActivities")
|
||||||
| {{{_ 'activity-attached' attachmentLink cardLabel}}}.
|
| {{{_ 'activity-attached' attachmentLink cardLabel}}}.
|
||||||
if attachment.isImage
|
if attachment.isImage
|
||||||
img.attachment-image-preview(src=attachment.url)
|
img.attachment-image-preview(src=attachment.url)
|
||||||
|
if($eq activityType 'addChecklist')
|
||||||
|
| {{{_ 'activity-checklist-added' cardLabel}}}.
|
||||||
|
.activity-checklist
|
||||||
|
+viewer
|
||||||
|
= checklist.title
|
||||||
|
|
||||||
if($eq activityType 'addComment')
|
if($eq activityType 'addComment')
|
||||||
+inlinedForm(classNames='js-edit-comment')
|
+inlinedForm(classNames='js-edit-comment')
|
||||||
|
|
|
||||||
|
|
@ -26,6 +26,14 @@
|
||||||
margin-top: 5px
|
margin-top: 5px
|
||||||
padding: 5px
|
padding: 5px
|
||||||
|
|
||||||
|
.activity-checklist
|
||||||
|
display: block
|
||||||
|
border-radius: 3px
|
||||||
|
background: white
|
||||||
|
text-decoration: none
|
||||||
|
box-shadow: 0 1px 2px rgba(0,0,0,.2)
|
||||||
|
margin-top: 5px
|
||||||
|
padding: 5px
|
||||||
.activity-meta
|
.activity-meta
|
||||||
font-size: 0.8em
|
font-size: 0.8em
|
||||||
color: darken(white, 40%)
|
color: darken(white, 40%)
|
||||||
|
|
|
||||||
|
|
@ -72,6 +72,10 @@ template(name="cardDetails")
|
||||||
h3.card-details-item-title {{_ 'description'}}
|
h3.card-details-item-title {{_ 'description'}}
|
||||||
+viewer
|
+viewer
|
||||||
= description
|
= description
|
||||||
|
|
||||||
|
hr
|
||||||
|
+checklists(cardId = _id)
|
||||||
|
|
||||||
if attachments.count
|
if attachments.count
|
||||||
hr
|
hr
|
||||||
h2
|
h2
|
||||||
|
|
|
||||||
61
client/components/cards/checklists.jade
Normal file
61
client/components/cards/checklists.jade
Normal file
|
|
@ -0,0 +1,61 @@
|
||||||
|
template(name="checklists")
|
||||||
|
h2 {{_ 'checklists'}}
|
||||||
|
.card-checklist-items
|
||||||
|
each checklist in currentCard.checklists
|
||||||
|
+checklistDetail(checklist = checklist)
|
||||||
|
+inlinedForm(classNames="js-add-checklist" cardId = cardId)
|
||||||
|
+addChecklistItemForm
|
||||||
|
else
|
||||||
|
a.js-open-inlined-form
|
||||||
|
i.fa.fa-plus
|
||||||
|
| {{_ 'add-checklist'}}...
|
||||||
|
|
||||||
|
template(name="checklistDetail")
|
||||||
|
+inlinedForm(classNames="js-edit-checklist-title")
|
||||||
|
+editChecklistItemForm(checklist = checklist)
|
||||||
|
else
|
||||||
|
.checklist-title
|
||||||
|
.checkbox.fa.fa-check-square-o
|
||||||
|
a.js-delete-checklist {{_ "delete"}}...
|
||||||
|
span.checklist-stat(class="{{#if checklist.isFinished}}is-finished{{/if}}") {{checklist.finishedCount}}/{{checklist.itemCount}}
|
||||||
|
h2.title.js-open-inlined-form.is-editable {{checklist.title}}
|
||||||
|
+checklistItems(checklist = checklist)
|
||||||
|
|
||||||
|
template(name="addChecklistItemForm")
|
||||||
|
textarea.js-add-checklist-item(rows='1' autofocus)
|
||||||
|
.edit-controls.clearfix
|
||||||
|
button.primary.confirm.js-submit-add-checklist-item-form(type="submit") {{_ 'save'}}
|
||||||
|
a.fa.fa-times-thin.js-close-inlined-form
|
||||||
|
|
||||||
|
template(name="editChecklistItemForm")
|
||||||
|
textarea.js-edit-checklist-item(rows='1' autofocus)
|
||||||
|
if $eq type 'item'
|
||||||
|
= item.title
|
||||||
|
else
|
||||||
|
= checklist.title
|
||||||
|
.edit-controls.clearfix
|
||||||
|
button.primary.confirm.js-submit-edit-checklist-item-form(type="submit") {{_ 'save'}}
|
||||||
|
a.fa.fa-times-thin.js-close-inlined-form
|
||||||
|
span(title=createdAt) {{ moment createdAt }}
|
||||||
|
if currentUser.isBoardMember
|
||||||
|
a.js-delete-checklist-item {{_ "delete"}}...
|
||||||
|
|
||||||
|
template(name="checklistItems")
|
||||||
|
.checklist-items
|
||||||
|
each item in checklist.items
|
||||||
|
+inlinedForm(classNames="js-edit-checklist-item")
|
||||||
|
+editChecklistItemForm(type = 'item' item = item checklist = checklist)
|
||||||
|
else
|
||||||
|
+itemDetail(item = item checklist = checklist)
|
||||||
|
if currentUser.isBoardMember
|
||||||
|
+inlinedForm(classNames="js-add-checklist-item" checklist = checklist)
|
||||||
|
+addChecklistItemForm
|
||||||
|
else
|
||||||
|
a.add-checklist-item.js-open-inlined-form
|
||||||
|
i.fa.fa-plus
|
||||||
|
| {{_ 'add-checklist-item'}}...
|
||||||
|
|
||||||
|
template(name='itemDetail')
|
||||||
|
.item
|
||||||
|
.check-box.materialCheckBox(class="{{#if item.isFinished }}is-checked{{/if}}")
|
||||||
|
.item-title.js-open-inlined-form.is-editable(class="{{#if item.isFinished }}is-checked{{/if}}") {{item.title}}
|
||||||
74
client/components/cards/checklists.js
Normal file
74
client/components/cards/checklists.js
Normal file
|
|
@ -0,0 +1,74 @@
|
||||||
|
BlazeComponent.extendComponent({
|
||||||
|
addChecklist(event) {
|
||||||
|
event.preventDefault();
|
||||||
|
const textarea = this.find('textarea.js-add-checklist-item');
|
||||||
|
const title = textarea.value.trim();
|
||||||
|
const cardId = this.currentData().cardId;
|
||||||
|
Checklists.insert({
|
||||||
|
cardId,
|
||||||
|
title,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
addChecklistItem(event) {
|
||||||
|
event.preventDefault();
|
||||||
|
const textarea = this.find('textarea.js-add-checklist-item');
|
||||||
|
const title = textarea.value.trim();
|
||||||
|
const checklist = this.currentData().checklist;
|
||||||
|
checklist.addItem(title);
|
||||||
|
},
|
||||||
|
|
||||||
|
editChecklist(event) {
|
||||||
|
event.preventDefault();
|
||||||
|
const textarea = this.find('textarea.js-edit-checklist-item');
|
||||||
|
const title = textarea.value.trim();
|
||||||
|
const checklist = this.currentData().checklist;
|
||||||
|
checklist.setTitle(title);
|
||||||
|
},
|
||||||
|
|
||||||
|
editChecklistItem(event) {
|
||||||
|
event.preventDefault();
|
||||||
|
|
||||||
|
const textarea = this.find('textarea.js-edit-checklist-item');
|
||||||
|
const title = textarea.value.trim();
|
||||||
|
const itemId = this.currentData().item._id;
|
||||||
|
const checklist = this.currentData().checklist;
|
||||||
|
checklist.editItem(itemId, title);
|
||||||
|
},
|
||||||
|
|
||||||
|
deleteItem() {
|
||||||
|
const checklist = this.currentData().checklist;
|
||||||
|
const item = this.currentData().item;
|
||||||
|
if (checklist && item && item._id) {
|
||||||
|
checklist.removeItem(item._id);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
deleteChecklist() {
|
||||||
|
const checklist = this.currentData().checklist;
|
||||||
|
if (checklist && checklist._id) {
|
||||||
|
Checklists.remove(checklist._id);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
pressKey(event) {
|
||||||
|
//If user press enter key inside a form, submit it, so user doesn't have to leave keyboard to submit a form.
|
||||||
|
if (event.keyCode === 13) {
|
||||||
|
event.preventDefault();
|
||||||
|
const $form = $(event.currentTarget).closest('form');
|
||||||
|
$form.find('button[type=submit]').click();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
events() {
|
||||||
|
return [{
|
||||||
|
'submit .js-add-checklist': this.addChecklist,
|
||||||
|
'submit .js-edit-checklist-title': this.editChecklist,
|
||||||
|
'submit .js-add-checklist-item': this.addChecklistItem,
|
||||||
|
'submit .js-edit-checklist-item': this.editChecklistItem,
|
||||||
|
'click .js-delete-checklist-item': this.deleteItem,
|
||||||
|
'click .js-delete-checklist': this.deleteChecklist,
|
||||||
|
keydown: this.pressKey,
|
||||||
|
}];
|
||||||
|
},
|
||||||
|
}).register('checklists');
|
||||||
68
client/components/cards/checklists.styl
Normal file
68
client/components/cards/checklists.styl
Normal file
|
|
@ -0,0 +1,68 @@
|
||||||
|
.js-add-checklist
|
||||||
|
color: #8c8c8c
|
||||||
|
|
||||||
|
textarea.js-add-checklist-item, textarea.js-edit-checklist-item
|
||||||
|
overflow: hidden
|
||||||
|
word-wrap: break-word
|
||||||
|
resize: none
|
||||||
|
height: 34px
|
||||||
|
|
||||||
|
.delete-text
|
||||||
|
color: #8c8c8c
|
||||||
|
text-decoration: underline
|
||||||
|
word-wrap: break-word
|
||||||
|
float: right
|
||||||
|
padding-top: 6px
|
||||||
|
&:hover
|
||||||
|
color: inherit
|
||||||
|
|
||||||
|
.checklist-title
|
||||||
|
.checkbox
|
||||||
|
float: left
|
||||||
|
width: 30px
|
||||||
|
height 30px
|
||||||
|
font-size: 18px
|
||||||
|
line-height: 30px
|
||||||
|
|
||||||
|
.title
|
||||||
|
font-size: 18px
|
||||||
|
line-height: 30px
|
||||||
|
|
||||||
|
.checklist-stat
|
||||||
|
margin: 0 0.5em
|
||||||
|
float: right
|
||||||
|
padding-top: 6px
|
||||||
|
&.is-finished
|
||||||
|
color: #3cb500
|
||||||
|
|
||||||
|
.js-delete-checklist
|
||||||
|
@extends .delete-text
|
||||||
|
|
||||||
|
.checklist-items
|
||||||
|
margin: 0 0 0.5em 1.33em
|
||||||
|
|
||||||
|
.item
|
||||||
|
line-height: 25px
|
||||||
|
font-size: 1.1em
|
||||||
|
margin-top: 3px
|
||||||
|
display: flex
|
||||||
|
|
||||||
|
.check-box
|
||||||
|
margin-top: 5px
|
||||||
|
&.is-checked
|
||||||
|
border-bottom: 2px solid #3cb500
|
||||||
|
border-right: 2px solid #3cb500
|
||||||
|
|
||||||
|
.item-title
|
||||||
|
padding-left: 10px;
|
||||||
|
&.is-checked
|
||||||
|
color: #8c8c8c
|
||||||
|
font-style: italic
|
||||||
|
|
||||||
|
.js-delete-checklist-item
|
||||||
|
@extends .delete-text
|
||||||
|
padding: 12px 0 0 0
|
||||||
|
|
||||||
|
.add-checklist-item
|
||||||
|
padding-top: 0.5em
|
||||||
|
display: inline-block
|
||||||
|
|
@ -14,7 +14,7 @@ template(name="minicard")
|
||||||
.badges
|
.badges
|
||||||
if comments.count
|
if comments.count
|
||||||
.badge(title="{{_ 'card-comments-title' comments.count }}")
|
.badge(title="{{_ 'card-comments-title' comments.count }}")
|
||||||
span.badge-icon.fa.fa-comment-o
|
span.badge-icon.fa.fa-comment-o.badge-comment
|
||||||
span.badge-text= comments.count
|
span.badge-text= comments.count
|
||||||
if description
|
if description
|
||||||
.badge.badge-state-image-only(title=description)
|
.badge.badge-state-image-only(title=description)
|
||||||
|
|
@ -29,3 +29,8 @@ template(name="minicard")
|
||||||
if dueAt
|
if dueAt
|
||||||
.badge
|
.badge
|
||||||
+minicardDueDate
|
+minicardDueDate
|
||||||
|
if checklists.count
|
||||||
|
.badge(class="{{#if checklistFinished}}is-finished{{/if}}")
|
||||||
|
span.badge-icon.fa.fa-check-square-o
|
||||||
|
span.badge-text.check-list-text {{checklistFinishedCount}}/{{checklistItemCount}}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -99,10 +99,26 @@
|
||||||
.badge-text
|
.badge-text
|
||||||
vertical-align: middle
|
vertical-align: middle
|
||||||
|
|
||||||
|
&.is-finished
|
||||||
|
background: #3cb500
|
||||||
|
padding: 0px 3px
|
||||||
|
border-radius: 3px
|
||||||
|
color: white
|
||||||
|
|
||||||
|
.badge-icon,
|
||||||
|
.badge-text
|
||||||
|
vertical-align: middle//didn't figure why use top, it'd be easier to fill bg if it's middle. This was introduced in commit "91cfcf7b12b5e7c137c2e765b2c378dde6b82966" & "* Improve the design of the minicards badges" was mentioned.
|
||||||
|
&.badge-comment
|
||||||
|
margin-bottom: 0.1rem
|
||||||
|
|
||||||
.badge-text
|
.badge-text
|
||||||
font-size: 0.9em
|
font-size: 0.9em
|
||||||
padding-left: 2px
|
padding-left: 2px
|
||||||
line-height: 14px
|
line-height: 14px
|
||||||
|
.check-list-text
|
||||||
|
padding-left: 0px
|
||||||
|
line-height: 12px
|
||||||
|
|
||||||
|
|
||||||
.minicard-members
|
.minicard-members
|
||||||
float: right
|
float: right
|
||||||
|
|
|
||||||
|
|
@ -36,10 +36,13 @@
|
||||||
"activity-removed": "removed %s from %s",
|
"activity-removed": "removed %s from %s",
|
||||||
"activity-sent": "sent %s to %s",
|
"activity-sent": "sent %s to %s",
|
||||||
"activity-unjoined": "unjoined %s",
|
"activity-unjoined": "unjoined %s",
|
||||||
|
"activity-checklist-added": "added checklist to %s",
|
||||||
"add": "Add",
|
"add": "Add",
|
||||||
"add-attachment": "Add an attachment",
|
"add-attachment": "Add an attachment",
|
||||||
"add-board": "Add a new board",
|
"add-board": "Add a new board",
|
||||||
"add-card": "Add a card",
|
"add-card": "Add a card",
|
||||||
|
"add-checklist": "Add a checklist",
|
||||||
|
"add-checklist-item": "Add an item to checklist",
|
||||||
"add-cover": "Add Cover",
|
"add-cover": "Add Cover",
|
||||||
"add-label": "Add the label",
|
"add-label": "Add the label",
|
||||||
"add-list": "Add a list",
|
"add-list": "Add a list",
|
||||||
|
|
@ -115,6 +118,7 @@
|
||||||
"changePasswordPopup-title": "Change Password",
|
"changePasswordPopup-title": "Change Password",
|
||||||
"changePermissionsPopup-title": "Change Permissions",
|
"changePermissionsPopup-title": "Change Permissions",
|
||||||
"changeSettingsPopup-title": "Change Settings",
|
"changeSettingsPopup-title": "Change Settings",
|
||||||
|
"checklists": "Checklists",
|
||||||
"click-to-star": "Click to star this board.",
|
"click-to-star": "Click to star this board.",
|
||||||
"click-to-unstar": "Click to unstar this board.",
|
"click-to-unstar": "Click to unstar this board.",
|
||||||
"clipboard" : "Clipboard or drag & drop",
|
"clipboard" : "Clipboard or drag & drop",
|
||||||
|
|
|
||||||
|
|
@ -35,6 +35,9 @@ Activities.helpers({
|
||||||
attachment() {
|
attachment() {
|
||||||
return Attachments.findOne(this.attachmentId);
|
return Attachments.findOne(this.attachmentId);
|
||||||
},
|
},
|
||||||
|
checklist() {
|
||||||
|
return Checklists.findOne(this.checklistId);
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
Activities.before.insert((userId, doc) => {
|
Activities.before.insert((userId, doc) => {
|
||||||
|
|
@ -102,6 +105,10 @@ if (Meteor.isServer) {
|
||||||
const attachment = activity.attachment();
|
const attachment = activity.attachment();
|
||||||
params.attachment = attachment._id;
|
params.attachment = attachment._id;
|
||||||
}
|
}
|
||||||
|
if (activity.checklistId) {
|
||||||
|
const checklist = activity.checklist();
|
||||||
|
params.checklist = checklist.title;
|
||||||
|
}
|
||||||
if (board) {
|
if (board) {
|
||||||
const watchingUsers = _.pluck(_.where(board.watchers, {level: 'watching'}), 'userId');
|
const watchingUsers = _.pluck(_.where(board.watchers, {level: 'watching'}), 'userId');
|
||||||
const trackingUsers = _.pluck(_.where(board.watchers, {level: 'tracking'}), 'userId');
|
const trackingUsers = _.pluck(_.where(board.watchers, {level: 'tracking'}), 'userId');
|
||||||
|
|
|
||||||
|
|
@ -141,6 +141,36 @@ Cards.helpers({
|
||||||
return cover && cover.url() && cover;
|
return cover && cover.url() && cover;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
checklists() {
|
||||||
|
return Checklists.find({ cardId: this._id }, { sort: { createdAt: 1 }});
|
||||||
|
},
|
||||||
|
|
||||||
|
checklistItemCount() {
|
||||||
|
const checklists = this.checklists().fetch();
|
||||||
|
return checklists.map((checklist) => {
|
||||||
|
return checklist.itemCount();
|
||||||
|
}).reduce((prev, next) => {
|
||||||
|
return prev + next;
|
||||||
|
}, 0);
|
||||||
|
},
|
||||||
|
|
||||||
|
checklistFinishedCount() {
|
||||||
|
const checklists = this.checklists().fetch();
|
||||||
|
return checklists.map((checklist) => {
|
||||||
|
return checklist.finishedCount();
|
||||||
|
}).reduce((prev, next) => {
|
||||||
|
return prev + next;
|
||||||
|
}, 0);
|
||||||
|
},
|
||||||
|
|
||||||
|
checklistFinished() {
|
||||||
|
return this.hasChecklist() && this.checklistItemCount() === this.checklistFinishedCount();
|
||||||
|
},
|
||||||
|
|
||||||
|
hasChecklist() {
|
||||||
|
return this.checklistItemCount() !== 0;
|
||||||
|
},
|
||||||
|
|
||||||
absoluteUrl() {
|
absoluteUrl() {
|
||||||
const board = this.board();
|
const board = this.board();
|
||||||
return FlowRouter.url('card', {
|
return FlowRouter.url('card', {
|
||||||
|
|
|
||||||
164
models/checklists.js
Normal file
164
models/checklists.js
Normal file
|
|
@ -0,0 +1,164 @@
|
||||||
|
Checklists = new Mongo.Collection('checklists');
|
||||||
|
|
||||||
|
Checklists.attachSchema(new SimpleSchema({
|
||||||
|
cardId: {
|
||||||
|
type: String,
|
||||||
|
},
|
||||||
|
title: {
|
||||||
|
type: String,
|
||||||
|
},
|
||||||
|
items: {
|
||||||
|
type: [Object],
|
||||||
|
defaultValue: [],
|
||||||
|
},
|
||||||
|
'items.$._id': {
|
||||||
|
type: String,
|
||||||
|
},
|
||||||
|
'items.$.title': {
|
||||||
|
type: String,
|
||||||
|
},
|
||||||
|
'items.$.isFinished': {
|
||||||
|
type: Boolean,
|
||||||
|
defaultValue: false,
|
||||||
|
},
|
||||||
|
finishedAt: {
|
||||||
|
type: Date,
|
||||||
|
optional: true,
|
||||||
|
},
|
||||||
|
createdAt: {
|
||||||
|
type: Date,
|
||||||
|
denyUpdate: false,
|
||||||
|
},
|
||||||
|
}));
|
||||||
|
|
||||||
|
Checklists.helpers({
|
||||||
|
itemCount () {
|
||||||
|
return this.items.length;
|
||||||
|
},
|
||||||
|
finishedCount () {
|
||||||
|
return this.items.filter((item) => {
|
||||||
|
return item.isFinished;
|
||||||
|
}).length;
|
||||||
|
},
|
||||||
|
isFinished () {
|
||||||
|
return 0 !== this.itemCount() && this.itemCount() === this.finishedCount();
|
||||||
|
},
|
||||||
|
getItem (_id) {
|
||||||
|
return _.findWhere(this.items, { _id });
|
||||||
|
},
|
||||||
|
itemIndex(itemId) {
|
||||||
|
return _.pluck(this.items, '_id').indexOf(itemId);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
Checklists.allow({
|
||||||
|
insert(userId, doc) {
|
||||||
|
return allowIsBoardMemberByCard(userId, Cards.findOne(doc.cardId));
|
||||||
|
},
|
||||||
|
update(userId, doc) {
|
||||||
|
return allowIsBoardMemberByCard(userId, Cards.findOne(doc.cardId));
|
||||||
|
},
|
||||||
|
remove(userId, doc) {
|
||||||
|
return allowIsBoardMemberByCard(userId, Cards.findOne(doc.cardId));
|
||||||
|
},
|
||||||
|
fetch: ['userId', 'cardId'],
|
||||||
|
});
|
||||||
|
|
||||||
|
Checklists.before.insert((userId, doc) => {
|
||||||
|
doc.createdAt = new Date();
|
||||||
|
if (!doc.userId) {
|
||||||
|
doc.userId = userId;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Checklists.mutations({
|
||||||
|
//for checklist itself
|
||||||
|
setTitle(title){
|
||||||
|
return { $set: { title }};
|
||||||
|
},
|
||||||
|
//for items in checklist
|
||||||
|
addItem(title) {
|
||||||
|
const itemCount = this.itemCount();
|
||||||
|
const _id = `${this._id}${itemCount}`;
|
||||||
|
return { $addToSet: {items: {_id, title, isFinished: false}} };
|
||||||
|
},
|
||||||
|
removeItem(itemId) {
|
||||||
|
return {$pull: {items: {_id : itemId}}};
|
||||||
|
},
|
||||||
|
editItem(itemId, title) {
|
||||||
|
if (this.getItem(itemId)) {
|
||||||
|
const itemIndex = this.itemIndex(itemId);
|
||||||
|
return {
|
||||||
|
$set: {
|
||||||
|
[`items.${itemIndex}.title`]: title,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return {};
|
||||||
|
},
|
||||||
|
finishItem(itemId) {
|
||||||
|
if (this.getItem(itemId)) {
|
||||||
|
const itemIndex = this.itemIndex(itemId);
|
||||||
|
return {
|
||||||
|
$set: {
|
||||||
|
[`items.${itemIndex}.isFinished`]: true,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return {};
|
||||||
|
},
|
||||||
|
resumeItem(itemId) {
|
||||||
|
if (this.getItem(itemId)) {
|
||||||
|
const itemIndex = this.itemIndex(itemId);
|
||||||
|
return {
|
||||||
|
$set: {
|
||||||
|
[`items.${itemIndex}.isFinished`]: false,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return {};
|
||||||
|
},
|
||||||
|
toggleItem(itemId) {
|
||||||
|
const item = this.getItem(itemId);
|
||||||
|
if (item) {
|
||||||
|
const itemIndex = this.itemIndex(itemId);
|
||||||
|
return {
|
||||||
|
$set: {
|
||||||
|
[`items.${itemIndex}.isFinished`]: !item.isFinished,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return {};
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
if (Meteor.isServer) {
|
||||||
|
Checklists.after.insert((userId, doc) => {
|
||||||
|
Activities.insert({
|
||||||
|
userId,
|
||||||
|
activityType: 'addChecklist',
|
||||||
|
cardId: doc.cardId,
|
||||||
|
boardId: Cards.findOne(doc.cardId).boardId,
|
||||||
|
checklistId: doc._id,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
//TODO: so there will be no activity for adding item into checklist, maybe will be implemented in the future.
|
||||||
|
// Checklists.after.update((userId, doc) => {
|
||||||
|
// console.log('update:', doc)
|
||||||
|
// Activities.insert({
|
||||||
|
// userId,
|
||||||
|
// activityType: 'addChecklist',
|
||||||
|
// boardId: doc.boardId,
|
||||||
|
// cardId: doc.cardId,
|
||||||
|
// checklistId: doc._id,
|
||||||
|
// });
|
||||||
|
// });
|
||||||
|
|
||||||
|
Checklists.before.remove((userId, doc) => {
|
||||||
|
const activity = Activities.findOne({ checklistId: doc._id });
|
||||||
|
if (activity) {
|
||||||
|
Activities.remove(activity._id);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
@ -5,3 +5,8 @@ allowIsBoardAdmin = function(userId, board) {
|
||||||
allowIsBoardMember = function(userId, board) {
|
allowIsBoardMember = function(userId, board) {
|
||||||
return board && board.hasMember(userId);
|
return board && board.hasMember(userId);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
allowIsBoardMemberByCard = function(userId, card) {
|
||||||
|
const board = card.board();
|
||||||
|
return board && board.hasMember(userId);
|
||||||
|
};
|
||||||
|
|
|
||||||
|
|
@ -98,6 +98,7 @@ Meteor.publishRelations('board', function(boardId) {
|
||||||
this.cursor(Cards.find({ boardId }), function(cardId) {
|
this.cursor(Cards.find({ boardId }), function(cardId) {
|
||||||
this.cursor(CardComments.find({ cardId }));
|
this.cursor(CardComments.find({ cardId }));
|
||||||
this.cursor(Attachments.find({ cardId }));
|
this.cursor(Attachments.find({ cardId }));
|
||||||
|
this.cursor(Checklists.find({ cardId }));
|
||||||
});
|
});
|
||||||
|
|
||||||
if (board.members) {
|
if (board.members) {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue