diff options
author | Lauri Ojansivu <x@xet7.org> | 2017-11-21 10:03:02 +0200 |
---|---|---|
committer | Lauri Ojansivu <x@xet7.org> | 2017-11-21 10:03:02 +0200 |
commit | e162fe3c0fc425efe925dd15f55771f9a3ee60f3 (patch) | |
tree | b2015f2a7b8d099aa554027e482c8f9676a960d3 | |
parent | f3e7646cfa065ceea0cf1aaff1f5adbde457a32a (diff) | |
parent | 6dba4ccd4d0c8d7443e7d9c39ddafed2b8f1b6ca (diff) | |
download | wekan-e162fe3c0fc425efe925dd15f55771f9a3ee60f3.tar.gz wekan-e162fe3c0fc425efe925dd15f55771f9a3ee60f3.tar.bz2 wekan-e162fe3c0fc425efe925dd15f55771f9a3ee60f3.zip |
Merge branch 'card-spent-time' of https://github.com/thuanpq/wekan into thuanpq-card-spent-time
-rw-r--r-- | client/components/boards/boardsList.jade | 6 | ||||
-rw-r--r-- | client/components/boards/boardsList.js | 12 | ||||
-rw-r--r-- | client/components/boards/boardsList.styl | 17 | ||||
-rw-r--r-- | client/components/cards/cardDetails.jade | 9 | ||||
-rw-r--r-- | client/components/cards/cardDetails.js | 1 | ||||
-rw-r--r-- | client/components/cards/cardTime.jade | 22 | ||||
-rw-r--r-- | client/components/cards/cardTime.js | 81 | ||||
-rw-r--r-- | client/components/cards/cardTime.styl | 17 | ||||
-rw-r--r-- | client/components/cards/minicard.jade | 12 | ||||
-rw-r--r-- | i18n/en.i18n.json | 8 | ||||
-rw-r--r-- | models/boards.js | 10 | ||||
-rw-r--r-- | models/cards.js | 26 |
12 files changed, 215 insertions, 6 deletions
diff --git a/client/components/boards/boardsList.jade b/client/components/boards/boardsList.jade index ae82dfa9..95ce3678 100644 --- a/client/components/boards/boardsList.jade +++ b/client/components/boards/boardsList.jade @@ -20,6 +20,12 @@ template(name="boardList") i.fa.js-star-board( class="fa-star{{#if isStarred}} is-star-active{{else}}-o{{/if}}" title="{{_ 'star-board-title'}}") + + if hasSpentTimeCards + i.fa.js-has-spenttime-cards( + class="fa-circle{{#if hasOvertimeCards}} has-overtime-card-active{{else}} no-overtime-card-active{{/if}}" + title="{{#if hasOvertimeCards}}{{_ 'has-overtime-cards'}}{{else}}{{_ 'has-spenttime-cards'}}{{/if}}") + p.board-list-item-desc= description li.js-add-board a.board-list-item.label {{_ 'add-board'}} diff --git a/client/components/boards/boardsList.js b/client/components/boards/boardsList.js index e4bb050e..4ec4b534 100644 --- a/client/components/boards/boardsList.js +++ b/client/components/boards/boardsList.js @@ -1,3 +1,5 @@ +const subManager = new SubsManager(); + BlazeComponent.extendComponent({ boards() { return Boards.find({ @@ -13,6 +15,16 @@ BlazeComponent.extendComponent({ return user && user.hasStarred(this.currentData()._id); }, + hasOvertimeCards() { + subManager.subscribe('board', this.currentData()._id); + return this.currentData().hasOvertimeCards(); + }, + + hasSpentTimeCards() { + subManager.subscribe('board', this.currentData()._id); + return this.currentData().hasSpentTimeCards(); + }, + isInvited() { const user = Meteor.user(); return user && user.isInvitedTo(this.currentData()._id); diff --git a/client/components/boards/boardsList.styl b/client/components/boards/boardsList.styl index b4d21df6..6318ea94 100644 --- a/client/components/boards/boardsList.styl +++ b/client/components/boards/boardsList.styl @@ -74,6 +74,23 @@ $spaceBetweenTiles = 16px transition-duration: .15s transition-property: color, font-size, background + .fa-circle + bottom: 0; + font-size: 10px; + height: 10px; + line-height: 10px; + padding: 9px 9px; + position: absolute; + right: 0; + transition-duration: .15s + transition-property: color, font-size, background + + .has-overtime-card-active + color: #eb4646 !important + + .no-overtime-card-active + color: #3cb500 !important + .is-star-active color: white diff --git a/client/components/cards/cardDetails.jade b/client/components/cards/cardDetails.jade index 859b80c3..4c68c2f7 100644 --- a/client/components/cards/cardDetails.jade +++ b/client/components/cards/cardDetails.jade @@ -46,6 +46,14 @@ template(name="cardDetails") h3.card-details-item-title {{_ 'card-due'}} +cardDueDate + .card-details-items + if spentTime + .card-details-item.card-details-item-spent + if isOvertime + h3.card-details-item-title {{_ 'overtime-hours'}} + else + h3.card-details-item-title {{_ 'spent-time-hours'}} + +cardSpentTime //- XXX We should use "editable" to avoid repetiting ourselves if canModifyCard @@ -119,6 +127,7 @@ template(name="cardDetailsActionsPopup") li: a.js-attachments {{_ 'card-edit-attachments'}} li: a.js-start-date {{_ 'editCardStartDatePopup-title'}} li: a.js-due-date {{_ 'editCardDueDatePopup-title'}} + li: a.js-spent-time {{_ 'editCardSpentTimePopup-title'}} hr ul.pop-over-list li: a.js-move-card-to-top {{_ 'moveCardToTop-title'}} diff --git a/client/components/cards/cardDetails.js b/client/components/cards/cardDetails.js index c5b2fcdc..b62f31d4 100644 --- a/client/components/cards/cardDetails.js +++ b/client/components/cards/cardDetails.js @@ -165,6 +165,7 @@ Template.cardDetailsActionsPopup.events({ 'click .js-attachments': Popup.open('cardAttachments'), 'click .js-start-date': Popup.open('editCardStartDate'), 'click .js-due-date': Popup.open('editCardDueDate'), + 'click .js-spent-time': Popup.open('editCardSpentTime'), 'click .js-move-card': Popup.open('moveCard'), 'click .js-copy-card': Popup.open('copyCard'), 'click .js-move-card-to-top' (evt) { diff --git a/client/components/cards/cardTime.jade b/client/components/cards/cardTime.jade new file mode 100644 index 00000000..dcfc92f0 --- /dev/null +++ b/client/components/cards/cardTime.jade @@ -0,0 +1,22 @@ +template(name="editCardSpentTime") + .edit-card-time + form.edit-time + .fields + label(for="time") {{_ 'time'}} + input.js-time-field#time(type="number" step="0.01" name="time" value="{{card.spentTime}}" placeholder=timeFormat autofocus) + label(for="overtime") {{_ 'overtime'}} + a.js-toggle-overtime + .materialCheckBox#overtime(class="{{#if card.isOvertime}}is-checked{{/if}}" name="overtime") + + if error.get + .warning {{_ error.get}} + button.primary.wide.left.js-submit-time(type="submit") {{_ 'save'}} + button.js-delete-time.negate.wide.right {{_ 'delete'}} + +template(name="timeBadge") + if canModifyCard + a.js-edit-time.card-time(title="{{showTitle}}" class="{{#if isOvertime}}card-label-red{{else}}card-label-green{{/if}}") + | {{showTime}} + else + a.card-time(title="{{showTitle}}" class="{{#if isOvertime}}card-label-red{{else}}card-label-green{{/if}}") + | {{showTime}} diff --git a/client/components/cards/cardTime.js b/client/components/cards/cardTime.js new file mode 100644 index 00000000..eadcc88e --- /dev/null +++ b/client/components/cards/cardTime.js @@ -0,0 +1,81 @@ +BlazeComponent.extendComponent({ + template() { + return 'editCardSpentTime'; + }, + onCreated() { + this.error = new ReactiveVar(''); + this.card = this.data(); + }, + toggleOvertime() { + this.card.isOvertime = !this.card.isOvertime; + $('#overtime .materialCheckBox').toggleClass('is-checked'); + + $('#overtime').toggleClass('is-checked'); + }, + storeTime(spentTime, isOvertime) { + this.card.setSpentTime(spentTime); + this.card.setOvertime(isOvertime); + }, + deleteTime() { + this.card.unsetSpentTime(); + }, + events() { + return [{ + //TODO : need checking this portion + 'submit .edit-time'(evt) { + evt.preventDefault(); + + const spentTime = parseFloat(evt.target.time.value); + const isOvertime = this.card.isOvertime; + + if (spentTime >= 0) { + this.storeTime(spentTime, isOvertime); + Popup.close(); + } else { + this.error.set('invalid-time'); + evt.target.time.focus(); + } + }, + 'click .js-delete-time'(evt) { + evt.preventDefault(); + this.deleteTime(); + Popup.close(); + }, + 'click a.js-toggle-overtime': this.toggleOvertime, + }]; + }, +}).register('editCardSpentTimePopup'); + +BlazeComponent.extendComponent({ + template() { + return 'timeBadge'; + }, + onCreated() { + const self = this; + self.time = ReactiveVar(); + }, + showTitle() { + if (this.data().isOvertime) { + return `${TAPi18n.__('overtime')} ${this.data().spentTime} ${TAPi18n.__('hours')}`; + } else { + return `${TAPi18n.__('card-spent')} ${this.data().spentTime} ${TAPi18n.__('hours')}`; + } + }, + showTime() { + return this.data().spentTime; + }, + isOvertime() { + return this.data().isOvertime; + }, + events() { + return [{ + 'click .js-edit-time': Popup.open('editCardSpentTime'), + }]; + }, +}).register('cardSpentTime'); + +Template.timeBadge.helpers({ + canModifyCard() { + return Meteor.user() && Meteor.user().isBoardMember() && !Meteor.user().isCommentOnly(); + }, +}); diff --git a/client/components/cards/cardTime.styl b/client/components/cards/cardTime.styl new file mode 100644 index 00000000..3c4b43ae --- /dev/null +++ b/client/components/cards/cardTime.styl @@ -0,0 +1,17 @@ +.card-time + display: block + border-radius: 4px + padding: 1px 3px + color: #fff + + background-color: #dbdbdb + &:hover, &.is-active + background-color: #b3b3b3 + + time + &::before + font: normal normal normal 14px/1 FontAwesome + font-size: inherit + -webkit-font-smoothing: antialiased + content: "\f017" // clock symbol + margin-right: 0.3em diff --git a/client/components/cards/minicard.jade b/client/components/cards/minicard.jade index 3e582b6f..9fa4dd57 100644 --- a/client/components/cards/minicard.jade +++ b/client/components/cards/minicard.jade @@ -11,11 +11,15 @@ template(name="minicard") = title .dates if startAt - .date - +minicardStartDate + .date + +minicardStartDate if dueAt - .date - +minicardDueDate + .date + +minicardDueDate + if spentTime + .date + +cardSpentTime + if members .minicard-members.js-minicard-members each members diff --git a/i18n/en.i18n.json b/i18n/en.i18n.json index 5ec6a5f0..63e5be1c 100644 --- a/i18n/en.i18n.json +++ b/i18n/en.i18n.json @@ -103,6 +103,7 @@ "card-delete-suggest-archive": "You can archive a card to remove it from the board and preserve the activity.", "card-due": "Due", "card-due-on": "Due on", + "card-spent": "Spent Time", "card-edit-attachments": "Edit attachments", "card-edit-labels": "Edit labels", "card-edit-members": "Edit members", @@ -175,6 +176,7 @@ "soft-wip-limit": "Soft WIP Limit", "editCardStartDatePopup-title": "Change start date", "editCardDueDatePopup-title": "Change due date", + "editCardSpentTimePopup-title": "Change spent time", "editLabelPopup-title": "Change Label", "editNotificationPopup-title": "Edit Notification", "editProfilePopup-title": "Edit Profile", @@ -236,6 +238,7 @@ "info": "Version", "initials": "Initials", "invalid-date": "Invalid date", + "invalid-time": "Invalid time", "joined": "joined", "just-invited": "You are just invited to this board", "keyboard-shortcuts": "Keyboard shortcuts", @@ -337,6 +340,11 @@ "team": "Team", "this-board": "this board", "this-card": "this card", + "spent-time-hours": "Spent time (hours)", + "overtime-hours": "Overtime (hours)", + "overtime": "Overtime", + "has-overtime-cards": "Has overtime cards", + "has-spenttime-cards": "Has spenttime cards", "time": "Time", "title": "Title", "tracking": "Tracking", diff --git a/models/boards.js b/models/boards.js index 6ae818c6..594bb7b9 100644 --- a/models/boards.js +++ b/models/boards.js @@ -187,6 +187,16 @@ Boards.helpers({ return Lists.find({ boardId: this._id, archived: false }, { sort: { sort: 1 } }); }, + hasOvertimeCards(){ + const card = Cards.findOne({isOvertime: true, boardId: this._id, archived: false} ); + return card !== undefined; + }, + + hasSpentTimeCards(){ + const card = Cards.findOne({spentTime: { $gt: 0 }, boardId: this._id, archived: false} ); + return card !== undefined; + }, + activities() { return Activities.find({ boardId: this._id }, { sort: { createdAt: -1 } }); }, diff --git a/models/cards.js b/models/cards.js index b6397c9e..b62bfea8 100644 --- a/models/cards.js +++ b/models/cards.js @@ -64,8 +64,18 @@ Cards.attachSchema(new SimpleSchema({ type: Date, optional: true, }, - // XXX Should probably be called `authorId`. Is it even needed since we have - // the `members` field? + spentTime: { + type: Number, + decimal: true, + optional: true, + }, + isOvertime: { + type: Boolean, + defaultValue: false, + optional: true, + }, + // XXX Should probably be called `authorId`. Is it even needed since we have + // the `members` field? userId: { type: String, autoValue() { // eslint-disable-line consistent-return @@ -273,6 +283,18 @@ Cards.mutations({ unsetDue() { return {$unset: {dueAt: ''}}; }, + + setOvertime(isOvertime) { + return {$set: {isOvertime}}; + }, + + setSpentTime(spentTime) { + return {$set: {spentTime}}; + }, + + unsetSpentTime() { + return {$unset: {spentTime: '', isOvertime: false}}; + }, }); |