diff options
Diffstat (limited to 'client/components')
25 files changed, 717 insertions, 21 deletions
diff --git a/client/components/activities/activities.jade b/client/components/activities/activities.jade index 9ff73864..fad147fc 100644 --- a/client/components/activities/activities.jade +++ b/client/components/activities/activities.jade @@ -26,6 +26,12 @@ template(name="boardActivities") +viewer = comment.text + if($eq activityType 'addChecklist') + | {{{_ 'activity-checklist-added' cardLink}}}. + .activity-checklist(href="{{ card.absoluteUrl }}") + +viewer + = checklist.title + if($eq activityType 'archivedCard') | {{{_ 'activity-archived' cardLink}}}. @@ -41,6 +47,9 @@ template(name="boardActivities") if($eq activityType 'createList') | {{_ 'activity-added' list.title boardLabel}}. + if($eq activityType 'removeList') + | {{_ 'activity-removed' title boardLabel}}. + if($eq activityType 'importBoard') | {{{_ 'activity-imported-board' boardLabel sourceLink}}}. @@ -103,6 +112,11 @@ template(name="cardActivities") | {{{_ 'activity-attached' attachmentLink cardLabel}}}. if attachment.isImage img.attachment-image-preview(src=attachment.url) + if($eq activityType 'addChecklist') + | {{{_ 'activity-checklist-added' cardLabel}}}. + .activity-checklist + +viewer + = checklist.title if($eq activityType 'addComment') +inlinedForm(classNames='js-edit-comment') diff --git a/client/components/activities/activities.js b/client/components/activities/activities.js index 0bd6a272..6600849c 100644 --- a/client/components/activities/activities.js +++ b/client/components/activities/activities.js @@ -12,10 +12,12 @@ BlazeComponent.extendComponent({ const capitalizedMode = Utils.capitalize(mode); const id = Session.get(`current${capitalizedMode}`); const limit = this.page.get() * activitiesPerPage; + const user = Meteor.user(); + const hideSystem = user ? user.hasHiddenSystemMessages() : false; if (id === null) return; - this.subscribe('activities', mode, id, limit, () => { + this.subscribe('activities', mode, id, limit, hideSystem, () => { this.loadNextPageLocked = false; // If the sibear peak hasn't increased, that mean that there are no more diff --git a/client/components/activities/activities.styl b/client/components/activities/activities.styl index 1f0494c7..2285fc0a 100644 --- a/client/components/activities/activities.styl +++ b/client/components/activities/activities.styl @@ -26,6 +26,14 @@ margin-top: 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 font-size: 0.8em color: darken(white, 40%) diff --git a/client/components/boards/boardHeader.js b/client/components/boards/boardHeader.js index b72bb162..a76b566a 100644 --- a/client/components/boards/boardHeader.js +++ b/client/components/boards/boardHeader.js @@ -33,7 +33,7 @@ Template.boardChangeTitlePopup.events({ const newDesc = tpl.$('.js-board-desc').val().trim(); if (newTitle) { this.rename(newTitle); - this.setDesciption(newDesc); + this.setDescription(newDesc); Popup.close(); } evt.preventDefault(); diff --git a/client/components/cards/cardDate.jade b/client/components/cards/cardDate.jade new file mode 100644 index 00000000..a2a28bbd --- /dev/null +++ b/client/components/cards/cardDate.jade @@ -0,0 +1,20 @@ +template(name="editCardDate") + .edit-card-date + form.edit-date + .fields + .left + label(for="date") {{_ 'date'}} + input.js-date-field#date(type="text" name="date" value=showDate placeholder=dateFormat autofocus) + .right + label(for="time") {{_ 'time'}} + input.js-time-field#time(type="text" name="time" value=showTime placeholder=timeFormat) + .js-datepicker + if error.get + .warning {{_ error.get}} + button.primary.wide.left.js-submit-date(type="submit") {{_ 'save'}} + button.js-delete-date.negate.wide.right.js-delete-date {{_ 'delete'}} + +template(name="dateBadge") + a.js-edit-date.card-date(title="{{showTitle}}" class="{{classes}}") + time(datetime="{{showISODate}}") + | {{showDate}} diff --git a/client/components/cards/cardDate.js b/client/components/cards/cardDate.js new file mode 100644 index 00000000..4d129e8e --- /dev/null +++ b/client/components/cards/cardDate.js @@ -0,0 +1,228 @@ +// Edit start & due dates +const EditCardDate = BlazeComponent.extendComponent({ + template() { + return 'editCardDate'; + }, + + onCreated() { + this.error = new ReactiveVar(''); + this.card = this.data(); + this.date = new ReactiveVar(moment.invalid()); + }, + + onRendered() { + const $picker = this.$('.js-datepicker').datepicker({ + todayHighlight: true, + todayBtn: 'linked', + language: TAPi18n.getLanguage(), + }).on('changeDate', function(evt) { + this.find('#date').value = moment(evt.date).format('L'); + this.error.set(''); + this.find('#time').focus(); + }.bind(this)); + + if (this.date.get().isValid()) { + $picker.datepicker('update', this.date.get().toDate()); + } + }, + + showDate() { + if (this.date.get().isValid()) + return this.date.get().format('L'); + return ''; + }, + showTime() { + if (this.date.get().isValid()) + return this.date.get().format('LT'); + return ''; + }, + dateFormat() { + return moment.localeData().longDateFormat('L'); + }, + timeFormat() { + return moment.localeData().longDateFormat('LT'); + }, + + events() { + return [{ + 'keyup .js-date-field'() { + // parse for localized date format in strict mode + const dateMoment = moment(this.find('#date').value, 'L', true); + if (dateMoment.isValid()) { + this.error.set(''); + this.$('.js-datepicker').datepicker('update', dateMoment.toDate()); + } + }, + 'keyup .js-time-field'() { + // parse for localized time format in strict mode + const dateMoment = moment(this.find('#time').value, 'LT', true); + if (dateMoment.isValid()) { + this.error.set(''); + } + }, + 'submit .edit-date'(evt) { + evt.preventDefault(); + + // if no time was given, init with 12:00 + const time = evt.target.time.value || moment(new Date().setHours(12, 0, 0)).format('LT'); + + const dateString = `${evt.target.date.value} ${time}`; + const newDate = moment(dateString, 'L LT', true); + if (newDate.isValid()) { + this._storeDate(newDate.toDate()); + Popup.close(); + } + else { + this.error.set('invalid-date'); + evt.target.date.focus(); + } + }, + 'click .js-delete-date'(evt) { + evt.preventDefault(); + this._deleteDate(); + Popup.close(); + }, + }]; + }, +}); + +// editCardStartDatePopup +(class extends EditCardDate { + onCreated() { + super.onCreated(); + this.data().startAt && this.date.set(moment(this.data().startAt)); + } + + _storeDate(date) { + this.card.setStart(date); + } + + _deleteDate() { + this.card.unsetStart(); + } +}).register('editCardStartDatePopup'); + +// editCardDueDatePopup +(class extends EditCardDate { + onCreated() { + super.onCreated(); + this.data().dueAt && this.date.set(moment(this.data().dueAt)); + } + + onRendered() { + super.onRendered(); + if (moment.isDate(this.card.startAt)) { + this.$('.js-datepicker').datepicker('setStartDate', this.card.startAt); + } + } + + _storeDate(date) { + this.card.setDue(date); + } + + _deleteDate() { + this.card.unsetDue(); + } +}).register('editCardDueDatePopup'); + + +// Display start & due dates +const CardDate = BlazeComponent.extendComponent({ + template() { + return 'dateBadge'; + }, + + onCreated() { + const self = this; + self.date = ReactiveVar(); + self.now = ReactiveVar(moment()); + window.setInterval(() => { + self.now.set(moment()); + }, 60000); + }, + + showDate() { + // this will start working once mquandalle:moment + // is updated to at least moment.js 2.10.5 + // until then, the date is displayed in the "L" format + return this.date.get().calendar(null, { + sameElse: 'llll', + }); + }, + + showISODate() { + return this.date.get().toISOString(); + }, +}); + +class CardStartDate extends CardDate { + onCreated() { + super.onCreated(); + const self = this; + self.autorun(() => { + self.date.set(moment(self.data().startAt)); + }); + } + + classes() { + if (this.date.get().isBefore(this.now.get(), 'minute') && + this.now.get().isBefore(this.data().dueAt)) { + return 'current'; + } + return ''; + } + + showTitle() { + return `${TAPi18n.__('card-start-on')} ${this.date.get().format('LLLL')}`; + } + + events() { + return super.events().concat({ + 'click .js-edit-date': Popup.open('editCardStartDate'), + }); + } +} +CardStartDate.register('cardStartDate'); + +class CardDueDate extends CardDate { + onCreated() { + super.onCreated(); + const self = this; + self.autorun(() => { + self.date.set(moment(self.data().dueAt)); + }); + } + + classes() { + if (this.now.get().diff(this.date.get(), 'days') >= 2) + return 'long-overdue'; + else if (this.now.get().diff(this.date.get(), 'minute') >= 0) + return 'due'; + else if (this.now.get().diff(this.date.get(), 'days') >= -1) + return 'almost-due'; + return ''; + } + + showTitle() { + return `${TAPi18n.__('card-due-on')} ${this.date.get().format('LLLL')}`; + } + + events() { + return super.events().concat({ + 'click .js-edit-date': Popup.open('editCardDueDate'), + }); + } +} +CardDueDate.register('cardDueDate'); + +(class extends CardStartDate { + showDate() { + return this.date.get().format('l'); + } +}).register('minicardStartDate'); + +(class extends CardDueDate { + showDate() { + return this.date.get().format('l'); + } +}).register('minicardDueDate'); diff --git a/client/components/cards/cardDate.styl b/client/components/cards/cardDate.styl new file mode 100644 index 00000000..1631baa5 --- /dev/null +++ b/client/components/cards/cardDate.styl @@ -0,0 +1,58 @@ +.edit-card-date + .fields + .left + width: 56% + .right + width: 38% + .datepicker + width: 100% + table + width: 100% + border: none + border-spacing: 0 + border-collapse: collapse + thead + background: none + td, th + box-sizing: border-box + + +.card-date + display: block + border-radius: 4px + padding: 1px 3px + + background-color: #dbdbdb + &:hover, &.is-active + background-color: #b3b3b3 + + &.current, &.almost-due, &.due, &.long-overdue + color: #fff + + &.current + background-color: #5ba639 + &:hover, &.is-active + background-color: darken(#5ba639, 10) + + &.almost-due + background-color: #edc909 + &:hover, &.is-active + background-color: darken(#edc909, 10) + + &.due + background-color: #fa3f00 + &:hover, &.is-active + background-color: darken(#fa3f00, 10) + + &.long-overdue + background-color: #fd5d47 + &:hover, &.is-active + background-color: darken(#fd5d47, 7) + + time + &::before + font: normal normal normal 14px/1 FontAwesome + font-size: inherit + -webkit-font-smoothing: antialiased + content: "\f017" // clock symbol + margin-right: 0.3em
\ No newline at end of file diff --git a/client/components/cards/cardDetails.jade b/client/components/cards/cardDetails.jade index 734fc7e3..cf113951 100644 --- a/client/components/cards/cardDetails.jade +++ b/client/components/cards/cardDetails.jade @@ -35,6 +35,17 @@ template(name="cardDetails") a.card-label.add-label.js-add-labels(title="{{_ 'card-labels-title'}}") i.fa.fa-plus + if startAt + .card-details-item.card-details-item-start + h3.card-details-item-title {{_ 'card-start'}} + +cardStartDate + + if dueAt + .card-details-item.card-details-item-due + h3.card-details-item-title {{_ 'card-due'}} + +cardDueDate + + //- XXX We should use "editable" to avoid repetiting ourselves if currentUser.isBoardMember h3.card-details-item-title {{_ 'description'}} @@ -61,6 +72,10 @@ template(name="cardDetails") h3.card-details-item-title {{_ 'description'}} +viewer = description + + hr + +checklists(cardId = _id) + if attachments.count hr h2 @@ -91,6 +106,8 @@ template(name="cardDetailsActionsPopup") li: a.js-members {{_ 'card-edit-members'}} li: a.js-labels {{_ 'card-edit-labels'}} li: a.js-attachments {{_ 'card-edit-attachments'}} + li: a.js-start-date {{_ 'editCardStartDatePopup-title'}} + li: a.js-due-date {{_ 'editCardDueDatePopup-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 b6f17c23..b7e0ef76 100644 --- a/client/components/cards/cardDetails.js +++ b/client/components/cards/cardDetails.js @@ -65,6 +65,9 @@ BlazeComponent.extendComponent({ [`${CSSEvents.transitionend} .js-card-details`]() { this.isLoaded.set(true); }, + [`${CSSEvents.animationend} .js-card-details`]() { + this.isLoaded.set(true); + }, }; return [{ @@ -143,6 +146,8 @@ Template.cardDetailsActionsPopup.events({ 'click .js-members': Popup.open('cardMembers'), 'click .js-labels': Popup.open('cardLabels'), 'click .js-attachments': Popup.open('cardAttachments'), + 'click .js-start-date': Popup.open('editCardStartDate'), + 'click .js-due-date': Popup.open('editCardDueDate'), 'click .js-move-card': Popup.open('moveCard'), 'click .js-move-card-to-top'(evt) { evt.preventDefault(); diff --git a/client/components/cards/cardDetails.styl b/client/components/cards/cardDetails.styl index d7d29551..f209862c 100644 --- a/client/components/cards/cardDetails.styl +++ b/client/components/cards/cardDetails.styl @@ -73,8 +73,13 @@ margin: 15px 0 .card-details-item + margin-right: 0.5em + &:last-child + margin-right: 0 &.card-details-item-labels, - &.card-details-item-members + &.card-details-item-members, + &.card-details-item-start, + &.card-details-item-due width: 50% flex-shrink: 1 diff --git a/client/components/cards/checklists.jade b/client/components/cards/checklists.jade new file mode 100644 index 00000000..987e6cd9 --- /dev/null +++ b/client/components/cards/checklists.jade @@ -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" checklist = checklist) + +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" item = item checklist = checklist) + +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}} diff --git a/client/components/cards/checklists.js b/client/components/cards/checklists.js new file mode 100644 index 00000000..5dac7bd3 --- /dev/null +++ b/client/components/cards/checklists.js @@ -0,0 +1,89 @@ +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'); + +BlazeComponent.extendComponent({ + toggleItem() { + const checklist = this.currentData().checklist; + const item = this.currentData().item; + if (checklist && item && item._id) { + checklist.toggleItem(item._id); + } + }, + events() { + return [{ + 'click .item .check-box': this.toggleItem, + }]; + }, +}).register('itemDetail'); diff --git a/client/components/cards/checklists.styl b/client/components/cards/checklists.styl new file mode 100644 index 00000000..885d7528 --- /dev/null +++ b/client/components/cards/checklists.styl @@ -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 diff --git a/client/components/cards/minicard.jade b/client/components/cards/minicard.jade index 1dfd2f8e..8b46ee74 100644 --- a/client/components/cards/minicard.jade +++ b/client/components/cards/minicard.jade @@ -14,7 +14,7 @@ template(name="minicard") .badges if 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 if description .badge.badge-state-image-only(title=description) @@ -23,3 +23,14 @@ template(name="minicard") .badge span.badge-icon.fa.fa-paperclip span.badge-text= attachments.count + if startAt + .badge + +minicardStartDate + if dueAt + .badge + +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}} + diff --git a/client/components/cards/minicard.styl b/client/components/cards/minicard.styl index 0f6f8ad2..12a89785 100644 --- a/client/components/cards/minicard.styl +++ b/client/components/cards/minicard.styl @@ -91,15 +91,34 @@ margin-right: 11px margin-bottom: 3px font-size: 0.9em + + &:last-of-type + margin-right: 0 .badge-icon, .badge-text - vertical-align: top + 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 font-size: 0.9em padding-left: 2px line-height: 14px + .check-list-text + padding-left: 0px + line-height: 12px + .minicard-members float: right diff --git a/client/components/lists/list.js b/client/components/lists/list.js index e1909783..187d8ec3 100644 --- a/client/components/lists/list.js +++ b/client/components/lists/list.js @@ -44,7 +44,7 @@ BlazeComponent.extendComponent({ placeholder: 'minicard-wrapper placeholder', start(evt, ui) { ui.placeholder.height(ui.helper.height()); - EscapeActions.executeUpTo('popup'); + EscapeActions.executeUpTo('popup-close'); boardComponent.setIsDragging(true); }, stop(evt, ui) { diff --git a/client/components/lists/listHeader.jade b/client/components/lists/listHeader.jade index f9fe065f..29aa4282 100644 --- a/client/components/lists/listHeader.jade +++ b/client/components/lists/listHeader.jade @@ -6,6 +6,10 @@ template(name="listHeader") h2.list-header-name( class="{{#if currentUser.isBoardMember}}js-open-inlined-form is-editable{{/if}}") = title + if showCardsCountForList cards.count + = cards.count + span.lowercase + | {{_ 'cards'}} if currentUser.isBoardMember if isWatching i.list-header-watch-icon.fa.fa-eye @@ -29,6 +33,9 @@ template(name="listActionPopup") hr ul.pop-over-list li: a.js-close-list {{_ 'archive-list'}} + hr + ul.pop-over-list + li: a.js-remove-list {{_ 'remove-list'}} template(name="boardLists") ul.pop-over-list diff --git a/client/components/lists/listHeader.js b/client/components/lists/listHeader.js index c7ae8e62..f352022d 100644 --- a/client/components/lists/listHeader.js +++ b/client/components/lists/listHeader.js @@ -13,6 +13,14 @@ BlazeComponent.extendComponent({ return list.findWatcher(Meteor.userId()); }, + limitToShowCardsCount() { + return Meteor.user().getLimitToShowCardsCount(); + }, + + showCardsCountForList(count) { + return count > this.limitToShowCardsCount(); + }, + events() { return [{ 'click .js-open-list-menu': Popup.open('listAction'), @@ -52,4 +60,10 @@ Template.listActionPopup.events({ this.archive(); Popup.close(); }, + 'click .js-remove-list'(evt) { + const currentList = this; + evt.preventDefault(); + Lists.remove(currentList._id); + Popup.close(); + }, }); diff --git a/client/components/main/layouts.styl b/client/components/main/layouts.styl index 24b1bc5e..1bc92b42 100644 --- a/client/components/main/layouts.styl +++ b/client/components/main/layouts.styl @@ -3,7 +3,7 @@ global-reset() html, body, input, select, textarea, button - font: 14px Roboto, "Helvetica Neue", Arial, Helvetica, sans-serif + font: 14px Roboto, "Helvetica Neue", Arial, Helvetica, "Yu Gothic", "Meiryo", sans-serif line-height: 18px color: #4d4d4d @@ -379,3 +379,8 @@ a .wrapper height: 100% margin: 0px + +.inline-input + height: 37px + margin: 8px 10px 0 0 + width: 50px diff --git a/client/components/sidebar/sidebar.jade b/client/components/sidebar/sidebar.jade index 4f5586cb..f3fdd1bc 100644 --- a/client/components/sidebar/sidebar.jade +++ b/client/components/sidebar/sidebar.jade @@ -30,10 +30,13 @@ template(name="membersWidget") .board-widget-content each currentBoard.activeMembers +userAvatar(userId=this.userId showStatus=true) - unless isSandstorm - if currentUser.isBoardAdmin - a.member.add-member.js-manage-board-members + if isSandstorm + if currentUser.isBoardMember + a.member.add-member.sandstorm-powerbox-request-identity i.fa.fa-plus + else if currentUser.isBoardAdmin + a.member.add-member.js-manage-board-members + i.fa.fa-plus .clearfix if isInvited hr diff --git a/client/components/sidebar/sidebar.js b/client/components/sidebar/sidebar.js index 0af32f8f..5e696308 100644 --- a/client/components/sidebar/sidebar.js +++ b/client/components/sidebar/sidebar.js @@ -134,8 +134,8 @@ Template.memberPopup.events({ Popup.close(); }), 'click .js-leave-member'() { - const currentBoard = Boards.findOne(Session.get('currentBoard')); - Meteor.call('quitBoard', currentBoard, (err, ret) => { + const boardId = Session.get('currentBoard'); + Meteor.call('quitBoard', boardId, (err, ret) => { if (!ret && ret) { Popup.close(); FlowRouter.go('home'); @@ -163,6 +163,9 @@ Template.membersWidget.helpers({ Template.membersWidget.events({ 'click .js-member': Popup.open('member'), 'click .js-manage-board-members': Popup.open('addMember'), + 'click .sandstorm-powerbox-request-identity'() { + window.sandstormRequestIdentity(); + }, 'click .js-member-invite-accept'() { const boardId = Session.get('currentBoard'); Meteor.user().removeInvite(boardId); diff --git a/client/components/sidebar/sidebar.styl b/client/components/sidebar/sidebar.styl index 9007af59..24abe990 100644 --- a/client/components/sidebar/sidebar.styl +++ b/client/components/sidebar/sidebar.styl @@ -51,6 +51,7 @@ .member, .card-label margin-right: 7px + margin-top: 5px .sidebar-list-item-description flex: 1 diff --git a/client/components/sidebar/sidebarFilters.jade b/client/components/sidebar/sidebarFilters.jade index 2b326493..9a9774bb 100644 --- a/client/components/sidebar/sidebarFilters.jade +++ b/client/components/sidebar/sidebarFilters.jade @@ -5,6 +5,12 @@ template(name="filterSidebar") ul.sidebar-list + li(class="{{#if Filter.labelIds.isSelected undefined}}active{{/if}}") + a.name.js-toggle-label-filter + span.sidebar-list-item-description + | {{_ 'filter-no-label'}} + if Filter.labelIds.isSelected undefined + i.fa.fa-check each currentBoard.labels li a.name.js-toggle-label-filter @@ -18,6 +24,12 @@ template(name="filterSidebar") i.fa.fa-check hr ul.sidebar-list + li(class="{{#if Filter.members.isSelected undefined}}active{{/if}}") + a.name.js-toggle-member-filter + span.sidebar-list-item-description + | {{_ 'filter-no-member'}} + if Filter.members.isSelected undefined + i.fa.fa-check each currentBoard.activeMembers with getUser userId li(class="{{#if Filter.members.isSelected _id}}active{{/if}}") @@ -86,4 +98,3 @@ template(name="disambiguateMultiMemberPopup") template(name="moveSelectionPopup") +boardLists - diff --git a/client/components/users/userHeader.jade b/client/components/users/userHeader.jade index a71e7fc7..ad41e8aa 100644 --- a/client/components/users/userHeader.jade +++ b/client/components/users/userHeader.jade @@ -12,10 +12,11 @@ template(name="memberMenuPopup") ul.pop-over-list with currentUser li: a.js-edit-profile {{_ 'edit-profile'}} - li: a.js-change-avatar {{_ 'edit-avatar'}} - li: a.js-change-password {{_ 'changePasswordPopup-title'}} - li: a.js-change-language {{_ 'changeLanguagePopup-title'}} - li: a.js-edit-notification {{_ 'editNotificationPopup-title'}} + li: a.js-change-settings {{_ 'change-settings'}} + li: a.js-change-avatar {{_ 'edit-avatar'}} + li: a.js-change-password {{_ 'changePasswordPopup-title'}} + li: a.js-change-language {{_ 'changeLanguagePopup-title'}} + li: a.js-edit-notification {{_ 'editNotificationPopup-title'}} hr ul.pop-over-list li: a.js-logout {{_ 'log-out'}} @@ -27,6 +28,8 @@ template(name="editProfilePopup") input.js-profile-fullname(type="text" value=profile.fullname autofocus) label | {{_ 'username'}} + span.error.hide.username-taken + | {{_ 'error-username-taken'}} input.js-profile-username(type="text" value=username) label | {{_ 'initials'}} @@ -61,3 +64,16 @@ template(name="changeLanguagePopup") = name if isCurrentLanguage i.fa.fa-check + +template(name="changeSettingsPopup") + ul.pop-over-list + li + a.js-toggle-system-messages + | {{_ 'hide-system-messages'}} + if hiddenSystemMessages + i.fa.fa-check + li + label.bold + | {{_ 'show-cards-minimum-count'}} + input#show-cards-count-at.inline-input.left(type="number" value="#{showCardsCountAt}" min="1" max="99" onkeydown="return false") + input.js-apply-show-cards-at.left(type="submit" value="{{_ 'apply'}}") diff --git a/client/components/users/userHeader.js b/client/components/users/userHeader.js index 10fdf699..98053ed1 100644 --- a/client/components/users/userHeader.js +++ b/client/components/users/userHeader.js @@ -5,6 +5,7 @@ Template.headerUserBar.events({ Template.memberMenuPopup.events({ 'click .js-edit-profile': Popup.open('editProfile'), + 'click .js-change-settings': Popup.open('changeSettings'), 'click .js-change-avatar': Popup.open('changeAvatar'), 'click .js-change-password': Popup.open('changePassword'), 'click .js-change-language': Popup.open('changeLanguage'), @@ -26,11 +27,18 @@ Template.editProfilePopup.events({ 'profile.fullname': fullname, 'profile.initials': initials, }}); - // XXX We should report the error to the user. + if (username !== Meteor.user().username) { - Meteor.call('setUsername', username); - } - Popup.back(); + Meteor.call('setUsername', username, function(error) { + const messageElement = tpl.$('.username-taken'); + if (error) { + messageElement.show(); + } else { + messageElement.hide(); + Popup.back(); + } + }); + } else Popup.back(); }, }); @@ -82,3 +90,26 @@ Template.changeLanguagePopup.events({ evt.preventDefault(); }, }); + +Template.changeSettingsPopup.helpers({ + hiddenSystemMessages() { + return Meteor.user().hasHiddenSystemMessages(); + }, + showCardsCountAt() { + return Meteor.user().getLimitToShowCardsCount(); + }, +}); + +Template.changeSettingsPopup.events({ + 'click .js-toggle-system-messages'() { + Meteor.call('toggleSystemMessages'); + }, + 'click .js-apply-show-cards-at'(evt, tpl) { + evt.preventDefault(); + const minLimit = parseInt(tpl.$('#show-cards-count-at').val(), 10); + if (!isNaN(minLimit)) { + Meteor.call('changeLimitToShowCardsCount', minLimit); + Popup.back(); + } + }, +}); |