diff options
author | Thiago Fernando <thiagofernando@outlook.com> | 2019-05-10 14:54:25 -0300 |
---|---|---|
committer | GitHub <noreply@github.com> | 2019-05-10 14:54:25 -0300 |
commit | ce0473480bab3fc621d4baecfff0f413e21b5e2c (patch) | |
tree | 06d724b4f80885bdd13137f7977d7a914cca0138 /client/components/boards | |
parent | c43508cacbd64357409a3de114db9dab2ae59a9d (diff) | |
parent | 7ff4067e88ed59686c86d81447fa2ce550032034 (diff) | |
download | wekan-ce0473480bab3fc621d4baecfff0f413e21b5e2c.tar.gz wekan-ce0473480bab3fc621d4baecfff0f413e21b5e2c.tar.bz2 wekan-ce0473480bab3fc621d4baecfff0f413e21b5e2c.zip |
Merge pull request #1 from wekan/devel
ldap changes
Diffstat (limited to 'client/components/boards')
-rw-r--r-- | client/components/boards/boardArchive.jade | 16 | ||||
-rw-r--r-- | client/components/boards/boardArchive.js | 17 | ||||
-rw-r--r-- | client/components/boards/boardBody.jade | 20 | ||||
-rw-r--r-- | client/components/boards/boardBody.js | 216 | ||||
-rw-r--r-- | client/components/boards/boardBody.styl | 7 | ||||
-rw-r--r-- | client/components/boards/boardHeader.jade | 203 | ||||
-rw-r--r-- | client/components/boards/boardHeader.js | 101 | ||||
-rw-r--r-- | client/components/boards/boardHeader.styl | 19 | ||||
-rw-r--r-- | client/components/boards/boardsList.jade | 13 | ||||
-rw-r--r-- | client/components/boards/boardsList.js | 36 | ||||
-rw-r--r-- | client/components/boards/boardsList.styl | 14 | ||||
-rw-r--r-- | client/components/boards/miniboard.jade | 8 |
12 files changed, 433 insertions, 237 deletions
diff --git a/client/components/boards/boardArchive.jade b/client/components/boards/boardArchive.jade index 6576f742..5d291f00 100644 --- a/client/components/boards/boardArchive.jade +++ b/client/components/boards/boardArchive.jade @@ -6,9 +6,17 @@ template(name="archivedBoards") ul.archived-lists each archivedBoards li.archived-lists-item - button.js-restore-board - i.fa.fa-undo - | {{_ 'restore-board'}} - = title + div.board-header-btns + button.board-header-btn.js-delete-board + i.fa.fa-trash-o + | {{_ 'delete-board'}} + button.board-header-btn.js-restore-board + i.fa.fa-undo + | {{_ 'restore-board'}} + = title else li.no-items-message {{_ 'no-archived-boards'}} + +template(name="boardDeletePopup") + p {{_ 'delete-board-confirm-popup'}} + button.js-confirm.negate.full(type="submit") {{_ 'delete'}} diff --git a/client/components/boards/boardArchive.js b/client/components/boards/boardArchive.js index acb53149..c8bbb341 100644 --- a/client/components/boards/boardArchive.js +++ b/client/components/boards/boardArchive.js @@ -1,9 +1,3 @@ -Template.boardListHeaderBar.events({ - 'click .js-open-archived-board'() { - Modal.open('archivedBoards'); - }, -}); - BlazeComponent.extendComponent({ onCreated() { this.subscribe('archivedBoards'); @@ -29,6 +23,17 @@ BlazeComponent.extendComponent({ board.restore(); Utils.goBoardId(board._id); }, + 'click .js-delete-board': Popup.afterConfirm('boardDelete', function() { + Popup.close(); + const isSandstorm = Meteor.settings && Meteor.settings.public && + Meteor.settings.public.sandstorm; + if (isSandstorm && Session.get('currentBoard')) { + const currentBoard = Boards.findOne(Session.get('currentBoard')); + Boards.remove(currentBoard._id); + } + Boards.remove(this._id); + FlowRouter.go('home'); + }), }]; }, }).register('archivedBoards'); diff --git a/client/components/boards/boardBody.jade b/client/components/boards/boardBody.jade index 29a613b9..017d0b0a 100644 --- a/client/components/boards/boardBody.jade +++ b/client/components/boards/boardBody.jade @@ -20,8 +20,22 @@ template(name="boardBody") class="{{#if draggingActive.get}}is-dragging-active{{/if}}") if showOverlay.get .board-overlay - if isViewSwimlanes + if currentBoard.isTemplatesBoard each currentBoard.swimlanes +swimlane(this) - if isViewLists - +listsGroup + else if isViewSwimlanes + each currentBoard.swimlanes + +swimlane(this) + else if isViewLists + +listsGroup(currentBoard) + else if isViewCalendar + +calendarView + else + +listsGroup(currentBoard) + +template(name="calendarView") + if isViewCalendar + .calendar-view.swimlane + if currentCard + +cardDetails(currentCard) + +fullcalendar(calendarOptions) diff --git a/client/components/boards/boardBody.js b/client/components/boards/boardBody.js index 456bf9b3..301c0742 100644 --- a/client/components/boards/boardBody.js +++ b/client/components/boards/boardBody.js @@ -1,5 +1,6 @@ const subManager = new SubsManager(); -const { calculateIndex } = Utils; +const { calculateIndex, enableClickOnTouch } = Utils; +const swimlaneWhileSortingHeight = 150; BlazeComponent.extendComponent({ onCreated() { @@ -35,6 +36,37 @@ BlazeComponent.extendComponent({ this._isDragging = false; // Used to set the overlay this.mouseHasEnterCardDetails = false; + + // fix swimlanes sort field if there are null values + const currentBoardData = Boards.findOne(Session.get('currentBoard')); + const nullSortSwimlanes = currentBoardData.nullSortSwimlanes(); + if (nullSortSwimlanes.count() > 0) { + const swimlanes = currentBoardData.swimlanes(); + let count = 0; + swimlanes.forEach((s) => { + Swimlanes.update(s._id, { + $set: { + sort: count, + }, + }); + count += 1; + }); + } + + // fix lists sort field if there are null values + const nullSortLists = currentBoardData.nullSortLists(); + if (nullSortLists.count() > 0) { + const lists = currentBoardData.lists(); + let count = 0; + lists.forEach((l) => { + Lists.update(l._id, { + $set: { + sort: count, + }, + }); + count += 1; + }); + } }, onRendered() { const boardComponent = this; @@ -43,21 +75,64 @@ BlazeComponent.extendComponent({ $swimlanesDom.sortable({ tolerance: 'pointer', appendTo: '.board-canvas', - helper: 'clone', + helper(evt, item) { + const helper = $(`<div class="swimlane" + style="flex-direction: column; + height: ${swimlaneWhileSortingHeight}px; + width: $(boardComponent.width)px; + overflow: hidden;"/>`); + helper.append(item.clone()); + // Also grab the list of lists of cards + const list = item.next(); + helper.append(list.clone()); + return helper; + }, handle: '.js-swimlane-header', - items: '.js-swimlane:not(.placeholder)', + items: '.swimlane:not(.placeholder)', placeholder: 'swimlane placeholder', distance: 7, start(evt, ui) { + const listDom = ui.placeholder.next('.js-swimlane'); + const parentOffset = ui.item.parent().offset(); + ui.placeholder.height(ui.helper.height()); EscapeActions.executeUpTo('popup-close'); + listDom.addClass('moving-swimlane'); boardComponent.setIsDragging(true); + + ui.placeholder.insertAfter(ui.placeholder.next()); + boardComponent.origPlaceholderIndex = ui.placeholder.index(); + + // resize all swimlanes + headers to be a total of 150 px per row + // this could be achieved by setIsDragging(true) but we want immediate + // result + ui.item.siblings('.js-swimlane').css('height', `${swimlaneWhileSortingHeight - 26}px`); + + // set the new scroll height after the resize and insertion of + // the placeholder. We want the element under the cursor to stay + // at the same place on the screen + ui.item.parent().get(0).scrollTop = ui.placeholder.get(0).offsetTop + parentOffset.top - evt.pageY; + }, + beforeStop(evt, ui) { + const parentOffset = ui.item.parent().offset(); + const siblings = ui.item.siblings('.js-swimlane'); + siblings.css('height', ''); + + // compute the new scroll height after the resize and removal of + // the placeholder + const scrollTop = ui.placeholder.get(0).offsetTop + parentOffset.top - evt.pageY; + + // then reset the original view of the swimlane + siblings.removeClass('moving-swimlane'); + + // and apply the computed scrollheight + ui.item.parent().get(0).scrollTop = scrollTop; }, stop(evt, ui) { // To attribute the new index number, we need to get the DOM element // of the previous and the following card -- if any. - const prevSwimlaneDom = ui.item.prev('.js-swimlane').get(0); - const nextSwimlaneDom = ui.item.next('.js-swimlane').get(0); + const prevSwimlaneDom = ui.item.prevAll('.js-swimlane').get(0); + const nextSwimlaneDom = ui.item.nextAll('.js-swimlane').get(0); const sortIndex = calculateIndex(prevSwimlaneDom, nextSwimlaneDom, 1); $swimlanesDom.sortable('cancel'); @@ -72,8 +147,35 @@ BlazeComponent.extendComponent({ boardComponent.setIsDragging(false); }, + sort(evt, ui) { + // get the mouse position in the sortable + const parentOffset = ui.item.parent().offset(); + const cursorY = evt.pageY - parentOffset.top + ui.item.parent().scrollTop(); + + // compute the intended index of the placeholder (we need to skip the + // slots between the headers and the list of cards) + const newplaceholderIndex = Math.floor(cursorY / swimlaneWhileSortingHeight); + let destPlaceholderIndex = (newplaceholderIndex + 1) * 2; + + // if we are scrolling far away from the bottom of the list + if (destPlaceholderIndex >= ui.item.parent().get(0).childElementCount) { + destPlaceholderIndex = ui.item.parent().get(0).childElementCount - 1; + } + + // update the placeholder position in the DOM tree + if (destPlaceholderIndex !== ui.placeholder.index()) { + if (destPlaceholderIndex < boardComponent.origPlaceholderIndex) { + ui.placeholder.insertBefore(ui.placeholder.siblings().slice(destPlaceholderIndex - 2, destPlaceholderIndex - 1)); + } else { + ui.placeholder.insertAfter(ui.placeholder.siblings().slice(destPlaceholderIndex - 1, destPlaceholderIndex)); + } + } + }, }); + // ugly touch event hotfix + enableClickOnTouch('.js-swimlane:not(.placeholder)'); + function userIsMember() { return Meteor.user() && Meteor.user().isBoardMember() && !Meteor.user().isCommentOnly(); } @@ -88,12 +190,20 @@ BlazeComponent.extendComponent({ isViewSwimlanes() { const currentUser = Meteor.user(); - return (currentUser.profile.boardView === 'board-view-swimlanes'); + if (!currentUser) return false; + return ((currentUser.profile || {}).boardView === 'board-view-swimlanes'); }, isViewLists() { const currentUser = Meteor.user(); - return (currentUser.profile.boardView === 'board-view-lists'); + if (!currentUser) return true; + return ((currentUser.profile || {}).boardView === 'board-view-lists'); + }, + + isViewCalendar() { + const currentUser = Meteor.user(); + if (!currentUser) return false; + return ((currentUser.profile || {}).boardView === 'board-view-cal'); }, openNewListForm() { @@ -105,7 +215,6 @@ BlazeComponent.extendComponent({ .childComponents('addListForm')[0].open(); } }, - events() { return [{ // XXX The board-overlay div should probably be moved to the parent @@ -137,4 +246,95 @@ BlazeComponent.extendComponent({ }); }, + scrollTop(position = 0) { + const swimlanes = this.$('.js-swimlanes'); + swimlanes && swimlanes.animate({ + scrollTop: position, + }); + }, + }).register('boardBody'); + +BlazeComponent.extendComponent({ + onRendered() { + this.autorun(function(){ + $('#calendar-view').fullCalendar('refetchEvents'); + }); + }, + calendarOptions() { + return { + id: 'calendar-view', + defaultView: 'agendaDay', + editable: true, + timezone: 'local', + header: { + left: 'title today prev,next', + center: 'agendaDay,listDay,timelineDay agendaWeek,listWeek,timelineWeek month,timelineMonth timelineYear', + right: '', + }, + // height: 'parent', nope, doesn't work as the parent might be small + height: 'auto', + /* TODO: lists as resources: https://fullcalendar.io/docs/vertical-resource-view */ + navLinks: true, + nowIndicator: true, + businessHours: { + // days of week. an array of zero-based day of week integers (0=Sunday) + dow: [ 1, 2, 3, 4, 5 ], // Monday - Friday + start: '8:00', + end: '18:00', + }, + locale: TAPi18n.getLanguage(), + events(start, end, timezone, callback) { + const currentBoard = Boards.findOne(Session.get('currentBoard')); + const events = []; + currentBoard.cardsInInterval(start.toDate(), end.toDate()).forEach(function(card){ + events.push({ + id: card._id, + title: card.title, + start: card.startAt, + end: card.endAt, + allDay: Math.abs(card.endAt.getTime() - card.startAt.getTime()) / 1000 === 24*3600, + url: FlowRouter.url('card', { + boardId: currentBoard._id, + slug: currentBoard.slug, + cardId: card._id, + }), + }); + }); + callback(events); + }, + eventResize(event, delta, revertFunc) { + let isOk = false; + const card = Cards.findOne(event.id); + + if (card) { + card.setEnd(event.end.toDate()); + isOk = true; + } + if (!isOk) { + revertFunc(); + } + }, + eventDrop(event, delta, revertFunc) { + let isOk = false; + const card = Cards.findOne(event.id); + if (card) { + // TODO: add a flag for allDay events + if (!event.allDay) { + card.setStart(event.start.toDate()); + card.setEnd(event.end.toDate()); + isOk = true; + } + } + if (!isOk) { + revertFunc(); + } + }, + }; + }, + isViewCalendar() { + const currentUser = Meteor.user(); + if (!currentUser) return false; + return ((currentUser.profile || {}).boardView === 'board-view-cal'); + }, +}).register('calendarView'); diff --git a/client/components/boards/boardBody.styl b/client/components/boards/boardBody.styl index a614c7ed..dfaaa050 100644 --- a/client/components/boards/boardBody.styl +++ b/client/components/boards/boardBody.styl @@ -15,12 +15,13 @@ position() .board-wrapper position: cover - overflow-y: hidden; + overflow-x: hidden + overflow-y: hidden .board-canvas position: cover transition: margin .1s - overflow-y: auto; + overflow-y: auto &.is-sibling-sidebar-open margin-right: 248px @@ -49,6 +50,6 @@ position() display: flex flex-direction: column margin: 0 - padding: 0 40px 0px 0 + padding: 0 0px 0px 0 overflow-x: hidden overflow-y: auto diff --git a/client/components/boards/boardHeader.jade b/client/components/boards/boardHeader.jade index fe0771cb..8bc61975 100644 --- a/client/components/boards/boardHeader.jade +++ b/client/components/boards/boardHeader.jade @@ -7,71 +7,69 @@ template(name="boardHeaderBar") .board-header-btns.left unless isMiniScreen - unless isSandstorm - if currentBoard - if currentUser - a.board-header-btn.js-star-board(class="{{#if isStarred}}is-active{{/if}}" - title="{{#if isStarred}}{{_ 'click-to-unstar'}}{{else}}{{_ 'click-to-star'}}{{/if}} {{_ 'starred-boards-description'}}") - i.fa(class="fa-star{{#unless isStarred}}-o{{/unless}}") - if showStarCounter - span - = currentBoard.stars - - a.board-header-btn( - class="{{#if currentUser.isBoardAdmin}}js-change-visibility{{else}}is-disabled{{/if}}" - title="{{_ currentBoard.permission}}") - i.fa(class="{{#if currentBoard.isPublic}}fa-globe{{else}}fa-lock{{/if}}") - span {{_ currentBoard.permission}} - - a.board-header-btn.js-watch-board( - title="{{_ watchLevel }}") - if $eq watchLevel "watching" - i.fa.fa-eye - if $eq watchLevel "tracking" - i.fa.fa-bell - if $eq watchLevel "muted" - i.fa.fa-bell-slash - span {{_ watchLevel}} + if currentBoard + if currentUser + a.board-header-btn.js-star-board(class="{{#if isStarred}}is-active{{/if}}" + title="{{#if isStarred}}{{_ 'click-to-unstar'}}{{else}}{{_ 'click-to-star'}}{{/if}} {{_ 'starred-boards-description'}}") + i.fa(class="fa-star{{#unless isStarred}}-o{{/unless}}") + if showStarCounter + span + = currentBoard.stars + + a.board-header-btn( + class="{{#if currentUser.isBoardAdmin}}js-change-visibility{{else}}is-disabled{{/if}}" + title="{{_ currentBoard.permission}}") + i.fa(class="{{#if currentBoard.isPublic}}fa-globe{{else}}fa-lock{{/if}}") + span {{_ currentBoard.permission}} + + a.board-header-btn.js-watch-board( + title="{{_ watchLevel }}") + if $eq watchLevel "watching" + i.fa.fa-eye + if $eq watchLevel "tracking" + i.fa.fa-bell + if $eq watchLevel "muted" + i.fa.fa-bell-slash + span {{_ watchLevel}} - else - a.board-header-btn.js-log-in( - title="{{_ 'log-in'}}") - i.fa.fa-sign-in - span {{_ 'log-in'}} + else + a.board-header-btn.js-log-in( + title="{{_ 'log-in'}}") + i.fa.fa-sign-in + span {{_ 'log-in'}} .board-header-btns.right if currentBoard if isMiniScreen - unless isSandstorm - if currentUser - a.board-header-btn.js-star-board(class="{{#if isStarred}}is-active{{/if}}" - title="{{#if isStarred}}{{_ 'click-to-unstar'}}{{else}}{{_ 'click-to-star'}}{{/if}} {{_ 'starred-boards-description'}}") - i.fa(class="fa-star{{#unless isStarred}}-o{{/unless}}") - if showStarCounter - span - = currentBoard.stars - - a.board-header-btn( - class="{{#if currentUser.isBoardAdmin}}js-change-visibility{{else}}is-disabled{{/if}}" - title="{{_ currentBoard.permission}}") - i.fa(class="{{#if currentBoard.isPublic}}fa-globe{{else}}fa-lock{{/if}}") - span {{_ currentBoard.permission}} - - a.board-header-btn.js-watch-board( - title="{{_ watchLevel }}") - if $eq watchLevel "watching" - i.fa.fa-eye - if $eq watchLevel "tracking" - i.fa.fa-bell - if $eq watchLevel "muted" - i.fa.fa-bell-slash - span {{_ watchLevel}} + if currentUser + a.board-header-btn.js-star-board(class="{{#if isStarred}}is-active{{/if}}" + title="{{#if isStarred}}{{_ 'click-to-unstar'}}{{else}}{{_ 'click-to-star'}}{{/if}} {{_ 'starred-boards-description'}}") + i.fa(class="fa-star{{#unless isStarred}}-o{{/unless}}") + if showStarCounter + span + = currentBoard.stars + + a.board-header-btn( + class="{{#if currentUser.isBoardAdmin}}js-change-visibility{{else}}is-disabled{{/if}}" + title="{{_ currentBoard.permission}}") + i.fa(class="{{#if currentBoard.isPublic}}fa-globe{{else}}fa-lock{{/if}}") + span {{_ currentBoard.permission}} + + a.board-header-btn.js-watch-board( + title="{{_ watchLevel }}") + if $eq watchLevel "watching" + i.fa.fa-eye + if $eq watchLevel "tracking" + i.fa.fa-bell + if $eq watchLevel "muted" + i.fa.fa-bell-slash + span {{_ watchLevel}} - else - a.board-header-btn.js-log-in( - title="{{_ 'log-in'}}") - i.fa.fa-sign-in - span {{_ 'log-in'}} + else + a.board-header-btn.js-log-in( + title="{{_ 'log-in'}}") + i.fa.fa-sign-in + span {{_ 'log-in'}} if isSandstorm if currentUser @@ -87,15 +85,20 @@ template(name="boardHeaderBar") if Filter.isActive a.board-header-btn-close.js-filter-reset(title="{{_ 'filter-clear'}}") i.fa.fa-times-thin + if currentUser.isAdmin + a.board-header-btn.js-open-rules-view(title="{{_ 'rules'}}") + i.fa.fa-magic + span {{_ 'rules'}} a.board-header-btn.js-open-search-view(title="{{_ 'search'}}") i.fa.fa-search span {{_ 'search'}} - a.board-header-btn.js-toggle-board-view( - title="{{_ 'board-view'}}") - i.fa.fa-th-large - span {{_ currentUser.profile.boardView}} + unless currentBoard.isTemplatesBoard + a.board-header-btn.js-toggle-board-view( + title="{{_ 'board-view'}}") + i.fa.fa-th-large + span {{#if currentUser.profile.boardView}}{{_ currentUser.profile.boardView}}{{else}}{{_ 'board-view-lists'}}{{/if}} if canModifyBoard a.board-header-btn.js-multiselection-activate( @@ -108,32 +111,8 @@ template(name="boardHeaderBar") i.fa.fa-times-thin .separator - a.board-header-btn.js-open-board-menu(title="{{_ 'boardMenuPopup-title'}}") - i.board-header-btn-icon.fa.fa-navicon - -template(name="boardMenuPopup") - ul.pop-over-list - li: a.js-open-archives {{_ 'archived-items'}} - if currentUser.isBoardAdmin - li: a.js-change-board-color {{_ 'board-change-color'}} - //- - XXX Language should be handled by sandstorm, but for now display a - language selection link in the board menu. This link is normally present - in the header bar that is not displayed on sandstorm. - if isSandstorm - li: a.js-change-language {{_ 'language'}} - unless isSandstorm - if currentUser.isBoardAdmin - hr - ul.pop-over-list - li: a(href="{{exportUrl}}", download="{{exportFilename}}") {{_ 'export-board'}} - li: a.js-archive-board {{_ 'archive-board'}} - li: a.js-outgoing-webhooks {{_ 'outgoing-webhooks'}} - if isSandstorm - hr - ul.pop-over-list - li: a(href="{{exportUrl}}", download="{{exportFilename}}") {{_ 'export-board'}} - li: a.js-import-board {{_ 'import-board-c'}} + a.board-header-btn.js-toggle-sidebar + i.fa.fa-navicon template(name="boardVisibilityList") ul.pop-over-list @@ -184,14 +163,6 @@ template(name="boardChangeWatchPopup") i.fa.fa-check span.sub-name {{_ 'muted-info'}} -template(name="boardChangeColorPopup") - .board-backgrounds-list.clearfix - each backgroundColors - .board-background-select.js-select-background - span.background-box(class="board-color-{{this}}") - if isSelected - i.fa.fa-check - template(name="createBoard") form label @@ -213,45 +184,21 @@ template(name="createBoard") input.primary.wide(type="submit" value="{{_ 'create'}}") span.quiet | {{_ 'or'}} - a.js-import-board {{_ 'import-board'}} - -template(name="chooseBoardSource") - ul.pop-over-list - li - a(href="{{pathFor '/import/trello'}}") {{_ 'from-trello'}} - li - a(href="{{pathFor '/import/wekan'}}") {{_ 'from-wekan'}} + a.js-import-board {{_ 'import'}} + span.quiet + | / + a.js-board-template {{_ 'template'}} template(name="boardChangeTitlePopup") form label | {{_ 'title'}} - input.js-board-name(type="text" value=title autofocus) + input.js-board-name(type="text" value=title autofocus dir="auto") label | {{_ 'description'}} - textarea.js-board-desc= description + textarea.js-board-desc(dir="auto")= description input.primary.wide(type="submit" value="{{_ 'rename'}}") -template(name="archiveBoardPopup") +template(name="boardCreateRulePopup") p {{_ 'close-board-pop'}} button.js-confirm.negate.full(type="submit") {{_ 'archive'}} - -template(name="outgoingWebhooksPopup") - each integrations - form.integration-form - if title - h4 {{title}} - else - h4 {{_ 'no-name'}} - label - | URL - input.js-outgoing-webhooks-url(type="text" name="url" value=url) - input(type="hidden" value=_id name="id") - input.primary.wide(type="submit" value="{{_ 'save'}}") - form.integration-form - h4 - | {{_ 'new-outgoing-webhook'}} - label - | URL - input.js-outgoing-webhooks-url(type="text" name="url" autofocus) - input.primary.wide(type="submit" value="{{_ 'save'}}") diff --git a/client/components/boards/boardHeader.js b/client/components/boards/boardHeader.js index 2b587831..f2b5c4f5 100644 --- a/client/components/boards/boardHeader.js +++ b/client/components/boards/boardHeader.js @@ -1,5 +1,9 @@ Template.boardMenuPopup.events({ 'click .js-rename-board': Popup.open('boardChangeTitle'), + 'click .js-custom-fields'() { + Sidebar.setView('customFields'); + Popup.close(); + }, 'click .js-open-archives'() { Sidebar.setView('archives'); Popup.close(); @@ -13,8 +17,15 @@ Template.boardMenuPopup.events({ // confirm that the board was successfully archived. FlowRouter.go('home'); }), + 'click .js-delete-board': Popup.afterConfirm('deleteBoard', function() { + const currentBoard = Boards.findOne(Session.get('currentBoard')); + Popup.close(); + Boards.remove(currentBoard._id); + FlowRouter.go('home'); + }), 'click .js-outgoing-webhooks': Popup.open('outgoingWebhooks'), 'click .js-import-board': Popup.open('chooseBoardSource'), + 'click .js-subtask-settings': Popup.open('boardSubtaskSettings'), }); Template.boardMenuPopup.helpers({ @@ -78,12 +89,19 @@ BlazeComponent.extendComponent({ }, 'click .js-toggle-board-view'() { const currentUser = Meteor.user(); - if (currentUser.profile.boardView === 'board-view-swimlanes') { + if ((currentUser.profile || {}).boardView === 'board-view-swimlanes') { + currentUser.setBoardView('board-view-cal'); + } else if ((currentUser.profile || {}).boardView === 'board-view-lists') { + currentUser.setBoardView('board-view-swimlanes'); + } else if ((currentUser.profile || {}).boardView === 'board-view-cal') { currentUser.setBoardView('board-view-lists'); - } else if (currentUser.profile.boardView === 'board-view-lists') { + } else { currentUser.setBoardView('board-view-swimlanes'); } }, + 'click .js-toggle-sidebar'() { + Sidebar.toggle(); + }, 'click .js-open-filter-view'() { Sidebar.setView('filter'); }, @@ -95,6 +113,9 @@ BlazeComponent.extendComponent({ 'click .js-open-search-view'() { Sidebar.setView('search'); }, + 'click .js-open-rules-view'() { + Modal.openWide('rulesMain'); + }, 'click .js-multiselection-activate'() { const currentCard = Session.get('currentCard'); MultiSelection.activate(); @@ -119,28 +140,6 @@ Template.boardHeaderBar.helpers({ }, }); -BlazeComponent.extendComponent({ - backgroundColors() { - return Boards.simpleSchema()._schema.color.allowedValues; - }, - - isSelected() { - const currentBoard = Boards.findOne(Session.get('currentBoard')); - return currentBoard.color === this.currentData().toString(); - }, - - events() { - return [{ - 'click .js-select-background'(evt) { - const currentBoard = Boards.findOne(Session.get('currentBoard')); - const newColor = this.currentData().toString(); - currentBoard.setColor(newColor); - evt.preventDefault(); - }, - }]; - }, -}).register('boardChangeColorPopup'); - const CreateBoard = BlazeComponent.extendComponent({ template() { return 'createBoard'; @@ -192,16 +191,11 @@ const CreateBoard = BlazeComponent.extendComponent({ 'click .js-import': Popup.open('boardImportBoard'), submit: this.onSubmit, 'click .js-import-board': Popup.open('chooseBoardSource'), + 'click .js-board-template': Popup.open('searchElement'), }]; }, }).register('createBoardPopup'); -BlazeComponent.extendComponent({ - template() { - return 'chooseBoardSource'; - }, -}).register('chooseBoardSourcePopup'); - (class HeaderBarCreateBoard extends CreateBoard { onSubmit(evt) { super.onSubmit(evt); @@ -251,50 +245,3 @@ BlazeComponent.extendComponent({ }]; }, }).register('boardChangeWatchPopup'); - -BlazeComponent.extendComponent({ - integrations() { - const boardId = Session.get('currentBoard'); - return Integrations.find({ boardId: `${boardId}` }).fetch(); - }, - - integration(id) { - const boardId = Session.get('currentBoard'); - return Integrations.findOne({ _id: id, boardId: `${boardId}` }); - }, - - events() { - return [{ - 'submit'(evt) { - evt.preventDefault(); - const url = evt.target.url.value; - const boardId = Session.get('currentBoard'); - let id = null; - let integration = null; - if (evt.target.id) { - id = evt.target.id.value; - integration = this.integration(id); - if (url) { - Integrations.update(integration._id, { - $set: { - url: `${url}`, - }, - }); - } else { - Integrations.remove(integration._id); - } - } else if (url) { - Integrations.insert({ - userId: Meteor.userId(), - enabled: true, - type: 'outgoing-webhooks', - url: `${url}`, - boardId: `${boardId}`, - activities: ['all'], - }); - } - Popup.close(); - }, - }]; - }, -}).register('outgoingWebhooksPopup'); diff --git a/client/components/boards/boardHeader.styl b/client/components/boards/boardHeader.styl index 0abdb5bd..402b4f1e 100644 --- a/client/components/boards/boardHeader.styl +++ b/client/components/boards/boardHeader.styl @@ -1,3 +1,22 @@ .integration-form padding: 5px border-bottom: 1px solid #ccc + +.flex + display: -webkit-box + display: -moz-box + display: -webkit-flex + display: -moz-flex + display: -ms-flexbox + display: flex + +.option + @extends .flex + -webkit-border-radius: 3px; + border-radius: 3px; + background: #fff; + text-decoration: none; + -webkit-box-shadow: 0 1px 2px rgba(0,0,0,0.2); + box-shadow: 0 1px 2px rgba(0,0,0,0.2); + margin-top: 5px; + padding: 5px; diff --git a/client/components/boards/boardsList.jade b/client/components/boards/boardsList.jade index 95ce3678..70b29c49 100644 --- a/client/components/boards/boardsList.jade +++ b/client/components/boards/boardsList.jade @@ -1,6 +1,8 @@ template(name="boardList") .wrapper ul.board-list.clearfix + li.js-add-board + a.board-list-item.label {{_ 'add-board'}} each boards li(class="{{#if isStarred}}starred{{/if}}" class=colorClass) if isInvited @@ -20,15 +22,15 @@ template(name="boardList") i.fa.js-star-board( class="fa-star{{#if isStarred}} is-star-active{{else}}-o{{/if}}" title="{{_ 'star-board-title'}}") - + p.board-list-item-desc= description 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}}") + i.fa.js-clone-board( + class="fa-clone" + title="{{_ 'duplicate-board'}}") - p.board-list-item-desc= description - li.js-add-board - a.board-list-item.label {{_ 'add-board'}} template(name="boardListHeaderBar") @@ -37,3 +39,6 @@ template(name="boardListHeaderBar") a.board-header-btn.js-open-archived-board i.fa.fa-archive span {{_ 'archives'}} + a.board-header-btn(href="{{pathFor 'board' id=templatesBoardId slug=templatesBoardSlug}}") + i.fa.fa-clone + span {{_ 'templates'}} diff --git a/client/components/boards/boardsList.js b/client/components/boards/boardsList.js index 1ed88146..8c45fbe2 100644 --- a/client/components/boards/boardsList.js +++ b/client/components/boards/boardsList.js @@ -1,5 +1,20 @@ const subManager = new SubsManager(); +Template.boardListHeaderBar.events({ + 'click .js-open-archived-board'() { + Modal.open('archivedBoards'); + }, +}); + +Template.boardListHeaderBar.helpers({ + templatesBoardId() { + return Meteor.user().getTemplatesBoardId(); + }, + templatesBoardSlug() { + return Meteor.user().getTemplatesBoardSlug(); + }, +}); + BlazeComponent.extendComponent({ onCreated() { Meteor.subscribe('setting'); @@ -9,11 +24,9 @@ BlazeComponent.extendComponent({ return Boards.find({ archived: false, 'members.userId': Meteor.userId(), - }, { - sort: ['title'], - }); + type: 'board', + }, { sort: ['title'] }); }, - isStarred() { const user = Meteor.user(); return user && user.hasStarred(this.currentData()._id); @@ -42,6 +55,21 @@ BlazeComponent.extendComponent({ Meteor.user().toggleBoardStar(boardId); evt.preventDefault(); }, + 'click .js-clone-board'(evt) { + Meteor.call('cloneBoard', + this.currentData()._id, + Session.get('fromBoard'), + (err, res) => { + if (err) { + this.setError(err.error); + } else { + Session.set('fromBoard', null); + Utils.goBoardId(res); + } + } + ); + evt.preventDefault(); + }, 'click .js-accept-invite'() { const boardId = this.currentData()._id; Meteor.user().removeInvite(boardId); diff --git a/client/components/boards/boardsList.styl b/client/components/boards/boardsList.styl index 80e47685..7e834411 100644 --- a/client/components/boards/boardsList.styl +++ b/client/components/boards/boardsList.styl @@ -94,13 +94,27 @@ $spaceBetweenTiles = 16px .is-star-active color: white + .fa-clone + position: absolute; + bottom: 0 + font-size: 14px + height: 18px + line-height: 18px + opacity: 0 + right: 0 + padding: 9px 9px + transition-duration: .15s + transition-property: color, font-size, background + li:hover a &:hover .fa-star, + .fa-clone, .fa-star-o color: white .fa-star, + .fa-clone, .fa-star-o color: white opacity: .75 diff --git a/client/components/boards/miniboard.jade b/client/components/boards/miniboard.jade new file mode 100644 index 00000000..d1fb0b07 --- /dev/null +++ b/client/components/boards/miniboard.jade @@ -0,0 +1,8 @@ +template(name="miniboard") + .minicard( + class="minicard-{{colorClass}}") + .minicard-title + .handle + .fa.fa-arrows + +viewer + = title |