summaryrefslogtreecommitdiffstats
path: root/client/components/boards
diff options
context:
space:
mode:
authorThiago Fernando <thiagofernando@outlook.com>2019-05-10 14:54:25 -0300
committerGitHub <noreply@github.com>2019-05-10 14:54:25 -0300
commitce0473480bab3fc621d4baecfff0f413e21b5e2c (patch)
tree06d724b4f80885bdd13137f7977d7a914cca0138 /client/components/boards
parentc43508cacbd64357409a3de114db9dab2ae59a9d (diff)
parent7ff4067e88ed59686c86d81447fa2ce550032034 (diff)
downloadwekan-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.jade16
-rw-r--r--client/components/boards/boardArchive.js17
-rw-r--r--client/components/boards/boardBody.jade20
-rw-r--r--client/components/boards/boardBody.js216
-rw-r--r--client/components/boards/boardBody.styl7
-rw-r--r--client/components/boards/boardHeader.jade203
-rw-r--r--client/components/boards/boardHeader.js101
-rw-r--r--client/components/boards/boardHeader.styl19
-rw-r--r--client/components/boards/boardsList.jade13
-rw-r--r--client/components/boards/boardsList.js36
-rw-r--r--client/components/boards/boardsList.styl14
-rw-r--r--client/components/boards/miniboard.jade8
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