From 2691f033cbd072864cf79e95d131a93449d3c84d Mon Sep 17 00:00:00 2001 From: Marc Hartmayer Date: Wed, 29 Apr 2020 22:38:50 +0200 Subject: Fix creation of card links Without this fix, orphaned card links are created and therefore this leads to problems as described in https://github.com/wekan/wekan/issues/2785. --- client/components/lists/listBody.js | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) (limited to 'client') diff --git a/client/components/lists/listBody.js b/client/components/lists/listBody.js index 88f88db0..1bcd41c4 100644 --- a/client/components/lists/listBody.js +++ b/client/components/lists/listBody.js @@ -658,10 +658,7 @@ BlazeComponent.extendComponent({ _id = element.copy(this.boardId, this.swimlaneId, this.listId); // 1.B Linked card } else { - delete element._id; - element.type = 'cardType-linkedCard'; - element.linkedId = element.linkedId || element._id; - _id = Cards.insert(element); + _id = element.link(this.boardId, this.swimlaneId, this.listId); } Filter.addException(_id); // List insertion -- cgit v1.2.3-1-g7c22 From b740381a7248e1e059cecedcf6cd6824abb792b3 Mon Sep 17 00:00:00 2001 From: Marc Hartmayer Date: Thu, 30 Apr 2020 01:03:37 +0200 Subject: Refuse to delete a card as long as there is link to it This fixes https://github.com/wekan/wekan/issues/2785. --- client/components/cards/cardDetails.js | 7 ++++++- client/components/lists/listHeader.js | 21 +++++++++++++++++++-- 2 files changed, 25 insertions(+), 3 deletions(-) (limited to 'client') diff --git a/client/components/cards/cardDetails.js b/client/components/cards/cardDetails.js index 271fbe2f..e8e36178 100644 --- a/client/components/cards/cardDetails.js +++ b/client/components/cards/cardDetails.js @@ -967,7 +967,12 @@ BlazeComponent.extendComponent({ }, 'click .js-delete': Popup.afterConfirm('cardDelete', function() { Popup.close(); - Cards.remove(this._id); + // verify that there are no linked cards + if (Cards.find({ linkedId: this._id }).count() === 0) { + Cards.remove(this._id); + } else { + // TODO popup... + } Utils.goBoardId(this.boardId); }), 'change .js-field-parent-board'(event) { diff --git a/client/components/lists/listHeader.js b/client/components/lists/listHeader.js index 46dbd748..a839bb72 100644 --- a/client/components/lists/listHeader.js +++ b/client/components/lists/listHeader.js @@ -223,8 +223,25 @@ BlazeComponent.extendComponent({ Template.listMorePopup.events({ 'click .js-delete': Popup.afterConfirm('listDelete', function() { Popup.close(); - this.allCards().map(card => Cards.remove(card._id)); - Lists.remove(this._id); + // TODO how can we avoid the fetch call? + const allCards = this.allCards().fetch(); + const allCardIds = _.pluck(allCards, '_id'); + // it's okay if the linked cards are on the same list + if ( + Cards.find({ + $and: [ + { listId: { $ne: this._id } }, + { linkedId: { $in: allCardIds } }, + ], + }).count() === 0 + ) { + allCardIds.map(_id => Cards.remove(_id)); + Lists.remove(this._id); + } else { + // TODO popup with a hint that the list cannot be deleted as there are + // linked cards. We can adapt the query above so we can list the linked + // cards. + } Utils.goBoardId(this.boardId); }), }); -- cgit v1.2.3-1-g7c22 From 9cba640120940eec45397d2daf8de573dbedf2b1 Mon Sep 17 00:00:00 2001 From: Marc Hartmayer Date: Thu, 30 Apr 2020 01:51:54 +0200 Subject: Fix typo --- client/components/lists/listBody.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'client') diff --git a/client/components/lists/listBody.js b/client/components/lists/listBody.js index 1bcd41c4..246e0156 100644 --- a/client/components/lists/listBody.js +++ b/client/components/lists/listBody.js @@ -672,7 +672,7 @@ BlazeComponent.extendComponent({ element.sort = Boards.findOne(this.boardId) .swimlanes() .count(); - element.type = 'swimlalne'; + element.type = 'swimlane'; _id = element.copy(this.boardId); } else if (this.isBoardTemplateSearch) { board = Boards.findOne(element.linkedId); -- cgit v1.2.3-1-g7c22 From 079867ff3770272cf72280f8abc261f89a96a80e Mon Sep 17 00:00:00 2001 From: helioguardabaxo Date: Sat, 2 May 2020 14:48:49 -0300 Subject: Add white-space:normal to copy-to-clipboard button in card details --- client/components/cards/cardDetails.jade | 2 +- client/components/cards/cardDetails.styl | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) (limited to 'client') diff --git a/client/components/cards/cardDetails.jade b/client/components/cards/cardDetails.jade index ae97e0e9..e9fb8b0f 100644 --- a/client/components/cards/cardDetails.jade +++ b/client/components/cards/cardDetails.jade @@ -519,7 +519,7 @@ template(name="cardMorePopup") = ' ' i.fa.colorful(class="{{#if board.isPublic}}fa-globe{{else}}fa-lock{{/if}}") input.inline-input(type="text" id="cardURL" readonly value="{{ absoluteUrl }}" autofocus="autofocus") - button.js-copy-card-link-to-clipboard(class="btn") {{_ 'copy-card-link-to-clipboard'}} + button.js-copy-card-link-to-clipboard(class="btn" id="clipboard") {{_ 'copy-card-link-to-clipboard'}} span.clearfix br h2 {{_ 'change-card-parent'}} diff --git a/client/components/cards/cardDetails.styl b/client/components/cards/cardDetails.styl index 3e2beadd..2d2d7ccf 100644 --- a/client/components/cards/cardDetails.styl +++ b/client/components/cards/cardDetails.styl @@ -10,6 +10,9 @@ avatar-radius = 50% left: -2000px top: 0px +#clipboard + white-space: normal + .assignee border-radius: 3px display: block -- cgit v1.2.3-1-g7c22 From 3cc0a93e0ea2399d239923e3a89d49d93a979684 Mon Sep 17 00:00:00 2001 From: Nico Date: Sun, 3 May 2020 00:33:15 +0200 Subject: Card vote options in new fork --- client/components/boards/boardsList.js | 3 +- client/components/cards/cardDate.js | 27 ++++++++ client/components/cards/cardDetails.jade | 79 +++++++++++++++--------- client/components/cards/cardDetails.js | 102 ++++++++++++++++++++++++------- client/components/cards/cardDetails.styl | 5 ++ client/components/cards/minicard.jade | 2 + client/components/main/editor.js | 2 +- 7 files changed, 166 insertions(+), 54 deletions(-) (limited to 'client') diff --git a/client/components/boards/boardsList.js b/client/components/boards/boardsList.js index 9208fdb2..b99c0c31 100644 --- a/client/components/boards/boardsList.js +++ b/client/components/boards/boardsList.js @@ -25,7 +25,6 @@ BlazeComponent.extendComponent({ }, onRendered() { - const self = this; function userIsAllowedToMove() { return Meteor.user(); } @@ -78,7 +77,7 @@ BlazeComponent.extendComponent({ }, boards() { - let query = { + const query = { archived: false, type: 'board', }; diff --git a/client/components/cards/cardDate.js b/client/components/cards/cardDate.js index c4b5c6d8..9b2268e9 100644 --- a/client/components/cards/cardDate.js +++ b/client/components/cards/cardDate.js @@ -386,3 +386,30 @@ CardEndDate.register('cardEndDate'); return this.date.get().format('l'); } }.register('minicardEndDate')); + +class VoteEndDate extends CardDate { + onCreated() { + super.onCreated(); + const self = this; + self.autorun(() => { + self.date.set(moment(self.data().getVoteEnd())); + }); + } + classes() { + const classes = 'end-date' + ' '; + return classes; + } + showDate() { + return this.date.get().format('l LT'); + } + showTitle() { + return `${TAPi18n.__('card-end-on')} ${this.date.get().format('LLLL')}`; + } + + events() { + return super.events().concat({ + 'click .js-edit-date': Popup.open('editVoteEndDate'), + }); + } +} +VoteEndDate.register('voteEndDate'); diff --git a/client/components/cards/cardDetails.jade b/client/components/cards/cardDetails.jade index ae97e0e9..9f3b188b 100644 --- a/client/components/cards/cardDetails.jade +++ b/client/components/cards/cardDetails.jade @@ -202,9 +202,12 @@ template(name="cardDetails") if getVoteQuestion hr .vote-title - h3 - i.fa.fa-thumbs-up - card-details-item-title {{_ 'vote-question'}} + div.flex + h3 + i.fa.fa-thumbs-up + | {{_ 'vote-question'}} + if getVoteEnd + +voteEndDate .vote-result if votePublic a.card-label.card-label-green.js-show-positive-votes {{ voteCountPositive }} @@ -212,10 +215,13 @@ template(name="cardDetails") else .card-label.card-label-green {{ voteCountPositive }} .card-label.card-label-red {{ voteCountNegative }} + unless ($and currentBoard.isPublic voteAllowNonBoardMembers ) + .card-label.card-label-gray {{ voteCount }} {{_ 'r-of' }} {{ currentBoard.activeMembers.length }} +viewer = getVoteQuestion - button.card-details-green.js-vote.js-vote-positive(class="{{#if voteState}}voted{{/if}}") {{_ 'vote-for-it'}} - button.card-details-red.js-vote.js-vote-negative(class="{{#if $eq voteState false}}voted{{/if}}") {{_ 'vote-against'}} + if showVotingButtons + button.card-details-green.js-vote.js-vote-positive(class="{{#if voteState}}voted{{/if}}") {{_ 'vote-for-it'}} + button.card-details-red.js-vote.js-vote-negative(class="{{#if $eq voteState false}}voted{{/if}}") {{_ 'vote-against'}} //- XXX We should use "editable" to avoid repetiting ourselves if canModifyCard @@ -333,16 +339,10 @@ template(name="cardDetailsActionsPopup") //li: a.js-members {{_ 'card-edit-members'}} //li: a.js-labels {{_ 'card-edit-labels'}} //li: a.js-attachments {{_ 'card-edit-attachments'}} - if getVoteQuestion - li - a.js-cancel-voting - i.fa.fa-thumbs-up - | {{_ 'card-cancel-voting'}} - else - li - a.js-start-voting - i.fa.fa-thumbs-up - | {{_ 'card-start-voting'}} + li + a.js-start-voting + i.fa.fa-thumbs-up + | {{_ 'card-edit-voting'}} li a.js-custom-fields i.fa.fa-list-alt @@ -465,14 +465,14 @@ template(name="cardAssigneesPopup") i.fa.fa-check if currentUser.isWorker ul.pop-over-list.js-card-assignee-list - li.item(class="{{#if currentUser.isCardAssignee}}active{{/if}}") - a.name.js-select-assignee(href="#") - +userAvatar(userId=currentUser._id) - span.full-name - = currentUser.profile.fullname - | ({{ currentUser.username }}) - if currentUser.isCardAssignee - i.fa.fa-check + li.item(class="{{#if currentUser.isCardAssignee}}active{{/if}}") + a.name.js-select-assignee(href="#") + +userAvatar(userId=currentUser._id) + span.full-name + = currentUser.profile.fullname + | ({{ currentUser.username }}) + if currentUser.isCardAssignee + i.fa.fa-check template(name="userAvatarAssignee") a.assignee.js-assignee(title="{{userData.profile.fullname}} ({{userData.username}})") @@ -564,20 +564,39 @@ template(name="setCardColorPopup") template(name="cardDeletePopup") p {{_ "card-delete-pop"}} unless archived - p {{_ "card-delete-suggest-archive"}} + p {{_ "card-delete-suggest-archive"}} + button.js-confirm.negate.full(type="submit") {{_ 'delete'}} + +template(name="deleteVotePopup") + p {{_ "vote-delete-pop"}} button.js-confirm.negate.full(type="submit") {{_ 'delete'}} template(name="cardStartVotingPopup") form.edit-vote-question .fields label(for="vote") {{_ 'vote-question'}} - input.js-vote-field#vote(type="text" name="vote" value="{{card.getVoteQuestion}}" autofocus) - label(for="vote-public") {{_ 'vote-public'}} - a.js-toggle-vote-public - .materialCheckBox#vote-public(name="vote-public") + input.js-vote-field#vote(type="text" name="vote" value="{{getVoteQuestion}}" autofocus disabled="{{#if getVoteQuestion}}disabled{{/if}}") + .check-div + a.flex(class="{{#if getVoteQuestion}}is-disabled{{else}}js-toggle-vote-allow-non-members{{/if}}") + .materialCheckBox#vote-allow-non-members(name="vote-allow-non-members" class="{{#if voteAllowNonBoardMembers}}is-checked{{/if}}") + span {{_ 'allowNonBoardMembers'}} + .check-div + a.flex(class="{{#if getVoteQuestion}}is-disabled{{else}}js-toggle-vote-public{{/if}}") + .materialCheckBox#vote-public(name="vote-public" class="{{#if votePublic}}is-checked{{/if}}") + span {{_ 'vote-public'}} + .check-div.flex + i.fa.fa-hourglass-end + a.js-end-date + span + | {{_ 'card-end'}} + unless getVoteEnd + i.fa.fa-plus + if getVoteEnd + +voteEndDate - button.primary.confirm.js-submit {{_ 'save'}} - //- button.js-remove-color.negate.wide.right {{_ 'delete'}} + button.primary.js-submit {{_ 'save'}} + if getVoteQuestion + button.js-remove-vote.negate.wide.right {{_ 'delete'}} template(name="positiveVoteMembersPopup") ul.pop-over-list.js-card-member-list diff --git a/client/components/cards/cardDetails.js b/client/components/cards/cardDetails.js index 271fbe2f..7dcadfe3 100644 --- a/client/components/cards/cardDetails.js +++ b/client/components/cards/cardDetails.js @@ -54,21 +54,6 @@ BlazeComponent.extendComponent({ } return null; }, - votePublic() { - const card = this.currentData(); - if (card.vote) return card.vote.public; - return null; - }, - voteCountPositive() { - const card = this.currentData(); - if (card.vote && card.vote.positive) return card.vote.positive.length; - return null; - }, - voteCountNegative() { - const card = this.currentData(); - if (card.vote && card.vote.negative) return card.vote.negative.length; - return null; - }, isWatching() { const card = this.currentData(); return card.findWatcher(Meteor.userId()); @@ -148,6 +133,15 @@ BlazeComponent.extendComponent({ return result; }, + showVotingButtons() { + const card = this.currentData(); + return ( + (currentUser.isBoardMember() || + (currentUser && card.voteAllowNonBoardMembers())) && + !card.expiredVote() + ); + }, + onRendered() { if (Meteor.settings.public.CARD_OPENED_WEBHOOK_ENABLED) { // Send Webhook but not create Activities records --- @@ -611,11 +605,6 @@ Template.cardDetailsActionsPopup.events({ 'click .js-copy-card': Popup.open('copyCard'), 'click .js-copy-checklist-cards': Popup.open('copyChecklistToManyCards'), 'click .js-set-card-color': Popup.open('setCardColor'), - 'click .js-cancel-voting'(event) { - event.preventDefault(); - this.unsetVote(); - Popup.close(); - }, 'click .js-move-card-to-top'(event) { event.preventDefault(); const minOrder = _.min( @@ -1000,22 +989,93 @@ BlazeComponent.extendComponent({ events() { return [ { + 'click .js-end-date': Popup.open('editVoteEndDate'), 'submit .edit-vote-question'(evt) { evt.preventDefault(); const voteQuestion = evt.target.vote.value; const publicVote = $('#vote-public').hasClass('is-checked'); - this.currentCard.setVoteQuestion(voteQuestion, publicVote); + const allowNonBoardMembers = $('#vote-allow-non-members').hasClass( + 'is-checked', + ); + const endString = this.currentCard.getVoteEnd(); + + this.currentCard.setVoteQuestion( + voteQuestion, + publicVote, + allowNonBoardMembers, + ); + if (endString) { + this.currentCard.setVoteEnd(endString); + } Popup.close(); }, + 'click .js-remove-vote': Popup.afterConfirm('deleteVote', () => { + event.preventDefault(); + this.currentCard.unsetVote(); + Popup.close(); + }), 'click a.js-toggle-vote-public'(event) { event.preventDefault(); $('#vote-public').toggleClass('is-checked'); }, + 'click a.js-toggle-vote-allow-non-members'(event) { + event.preventDefault(); + $('#vote-allow-non-members').toggleClass('is-checked'); + }, }, ]; }, }).register('cardStartVotingPopup'); +// editVoteEndDatePopup +(class extends DatePicker { + onCreated() { + super.onCreated(moment().format('YYYY-MM-DD HH:mm')); + this.data().getVoteEnd() && this.date.set(moment(this.data().getVoteEnd())); + } + events() { + return [ + { + '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()) { + // if active vote - store it + if (this.currentData().getVoteQuestion()) { + this._storeDate(newDate.toDate()); + Popup.close(); + } else { + this.currentData().vote = { end: newDate.toDate() }; // set vote end temp + Popup.back(); + } + } else { + this.error.set('invalid-date'); + evt.target.date.focus(); + } + }, + 'click .js-delete-date'(evt) { + evt.preventDefault(); + this._deleteDate(); + Popup.close(); + }, + }, + ]; + } + _storeDate(newDate) { + this.card.setVoteEnd(newDate); + } + _deleteDate() { + this.card.unsetVoteEnd(); + } +}.register('editVoteEndDatePopup')); + // Close the card details pane by pressing escape EscapeActions.register( 'detailsPane', diff --git a/client/components/cards/cardDetails.styl b/client/components/cards/cardDetails.styl index 3e2beadd..cfdc450d 100644 --- a/client/components/cards/cardDetails.styl +++ b/client/components/cards/cardDetails.styl @@ -337,6 +337,11 @@ card-details-color(background, color...) .vote-title display: flex justify-content: space-between + + .js-edit-date + align-self: baseline + margin-left: 5px + .vote-result display: flex .js-show-positive-votes diff --git a/client/components/cards/minicard.jade b/client/components/cards/minicard.jade index b6ccd4d7..79dd9127 100644 --- a/client/components/cards/minicard.jade +++ b/client/components/cards/minicard.jade @@ -103,7 +103,9 @@ template(name="minicard") if getVoteQuestion .badge.badge-state-image-only(title=getVoteQuestion) span.badge-icon.fa.fa-thumbs-up + span.badge-text {{ voteCountPositive }} span.badge-icon.fa.fa-thumbs-down + span.badge-text {{ voteCountNegative }} if attachments.count .badge span.badge-icon.fa.fa-paperclip diff --git a/client/components/main/editor.js b/client/components/main/editor.js index 081c6521..0c2e3186 100755 --- a/client/components/main/editor.js +++ b/client/components/main/editor.js @@ -330,7 +330,7 @@ Template.viewer.events({ // the corresponding text). Clicking a link shouldn't fire these actions, stop // we stop these event at the viewer component level. 'click a'(event, templateInstance) { - let prevent = true; + const prevent = true; const userId = event.currentTarget.dataset.userid; if (userId) { Popup.open('member').call({ userId }, event, templateInstance); -- cgit v1.2.3-1-g7c22 From 1742bcd9b15737c5853e9bcd0a6301139498307d Mon Sep 17 00:00:00 2001 From: Bryan Mutai Date: Thu, 7 May 2020 01:29:22 +0300 Subject: add: import board/cards/lists using CSV/TSV --- client/components/import/csvMembersMapper.js | 37 ++++++++++++++++++++ client/components/import/import.jade | 2 +- client/components/import/import.js | 52 +++++++++++++++++++++------- client/components/sidebar/sidebar.jade | 2 ++ 4 files changed, 80 insertions(+), 13 deletions(-) create mode 100644 client/components/import/csvMembersMapper.js (limited to 'client') diff --git a/client/components/import/csvMembersMapper.js b/client/components/import/csvMembersMapper.js new file mode 100644 index 00000000..cf8d5837 --- /dev/null +++ b/client/components/import/csvMembersMapper.js @@ -0,0 +1,37 @@ +export function getMembersToMap(data) { + // we will work on the list itself (an ordered array of objects) when a + // mapping is done, we add a 'wekan' field to the object representing the + // imported member + + const membersToMap = []; + const importedMembers = []; + let membersIndex; + + for (let i = 0; i < data[0].length; i++) { + if (data[0][i].toLowerCase() === 'members') { + membersIndex = i; + } + } + + for (let i = 1; i < data.length; i++) { + if (data[i][membersIndex]) { + for (const importedMember of data[i][membersIndex].split(' ')) { + if (importedMember && importedMembers.indexOf(importedMember) === -1) { + importedMembers.push(importedMember); + } + } + } + } + + for (let importedMember of importedMembers) { + importedMember = { + username: importedMember, + id: importedMember, + }; + const wekanUser = Users.findOne({ username: importedMember.username }); + if (wekanUser) importedMember.wekanId = wekanUser._id; + membersToMap.push(importedMember); + } + + return membersToMap; +} diff --git a/client/components/import/import.jade b/client/components/import/import.jade index 1551a7dd..2bea24ae 100644 --- a/client/components/import/import.jade +++ b/client/components/import/import.jade @@ -13,7 +13,7 @@ template(name="import") template(name="importTextarea") form p: label(for='import-textarea') {{_ instruction}} {{_ 'import-board-instruction-about-errors'}} - textarea.js-import-json(placeholder="{{_ 'import-json-placeholder'}}" autofocus) + textarea.js-import-json(placeholder="{{_ importPlaceHolder}}" autofocus) | {{jsonText}} input.primary.wide(type="submit" value="{{_ 'import'}}") diff --git a/client/components/import/import.js b/client/components/import/import.js index 6368885b..673900fd 100644 --- a/client/components/import/import.js +++ b/client/components/import/import.js @@ -1,5 +1,8 @@ import trelloMembersMapper from './trelloMembersMapper'; import wekanMembersMapper from './wekanMembersMapper'; +import csvMembersMapper from './csvMembersMapper'; + +const Papa = require('papaparse'); BlazeComponent.extendComponent({ title() { @@ -30,20 +33,30 @@ BlazeComponent.extendComponent({ } }, - importData(evt) { + importData(evt, dataSource) { evt.preventDefault(); - const dataJson = this.find('.js-import-json').value; - try { - const dataObject = JSON.parse(dataJson); - this.setError(''); - this.importedData.set(dataObject); - const membersToMap = this._prepareAdditionalData(dataObject); - // store members data and mapping in Session - // (we go deep and 2-way, so storing in data context is not a viable option) + const input = this.find('.js-import-json').value; + if (dataSource === 'csv') { + const csv = input.indexOf('\t') > 0 ? input.replace(/(\t)/g, ',') : input; + const ret = Papa.parse(csv); + if (ret && ret.data && ret.data.length) this.importedData.set(ret.data); + else throw new Meteor.Error('error-csv-schema'); + const membersToMap = this._prepareAdditionalData(ret.data); this.membersToMap.set(membersToMap); this.nextStep(); - } catch (e) { - this.setError('error-json-malformed'); + } else { + try { + const dataObject = JSON.parse(input); + this.setError(''); + this.importedData.set(dataObject); + const membersToMap = this._prepareAdditionalData(dataObject); + // store members data and mapping in Session + // (we go deep and 2-way, so storing in data context is not a viable option) + this.membersToMap.set(membersToMap); + this.nextStep(); + } catch (e) { + this.setError('error-json-malformed'); + } } }, @@ -91,6 +104,9 @@ BlazeComponent.extendComponent({ case 'wekan': membersToMap = wekanMembersMapper.getMembersToMap(dataObject); break; + case 'csv': + membersToMap = csvMembersMapper.getMembersToMap(dataObject); + break; } return membersToMap; }, @@ -109,11 +125,23 @@ BlazeComponent.extendComponent({ return `import-board-instruction-${Session.get('importSource')}`; }, + importPlaceHolder() { + const importSource = Session.get('importSource'); + if (importSource === 'csv') { + return 'import-csv-placeholder'; + } else { + return 'import-json-placeholder'; + } + }, + events() { return [ { submit(evt) { - return this.parentComponent().importData(evt); + return this.parentComponent().importData( + evt, + Session.get('importSource'), + ); }, }, ]; diff --git a/client/components/sidebar/sidebar.jade b/client/components/sidebar/sidebar.jade index 6bfedc9c..89622ac1 100644 --- a/client/components/sidebar/sidebar.jade +++ b/client/components/sidebar/sidebar.jade @@ -230,6 +230,8 @@ template(name="chooseBoardSource") a(href="{{pathFor '/import/trello'}}") {{_ 'from-trello'}} li a(href="{{pathFor '/import/wekan'}}") {{_ 'from-wekan'}} + li + a(href="{{pathFor '/import/csv'}}") {{_ 'from-csv'}} template(name="archiveBoardPopup") p {{_ 'close-board-pop'}} -- cgit v1.2.3-1-g7c22 From 2bb5a31fa4dffc412c9ac7bbe25205588fe5f28e Mon Sep 17 00:00:00 2001 From: Marco Volo Date: Fri, 8 May 2020 15:25:31 +0200 Subject: avatar-image fix --- client/components/cards/cardDetails.styl | 2 ++ client/components/users/userAvatar.styl | 2 ++ 2 files changed, 4 insertions(+) (limited to 'client') diff --git a/client/components/cards/cardDetails.styl b/client/components/cards/cardDetails.styl index 3e2beadd..4ca23f39 100644 --- a/client/components/cards/cardDetails.styl +++ b/client/components/cards/cardDetails.styl @@ -37,6 +37,8 @@ avatar-radius = 50% position: absolute &.avatar-image + object-fit: cover; + object-position: center; height: 100% width: @height diff --git a/client/components/users/userAvatar.styl b/client/components/users/userAvatar.styl index b962b01c..8209f4a1 100644 --- a/client/components/users/userAvatar.styl +++ b/client/components/users/userAvatar.styl @@ -29,6 +29,8 @@ avatar-radius = 50% position: absolute &.avatar-image + object-fit: cover; + object-position: center; height: 100% width: @height -- cgit v1.2.3-1-g7c22 From a570c4a86157ce4b60e056a4f0583ebc0fe009cf Mon Sep 17 00:00:00 2001 From: Bryan Mutai Date: Sun, 10 May 2020 23:58:15 +0300 Subject: add: export board/cards/lists to CSV/TSV --- client/components/sidebar/sidebar.jade | 17 +++++++++- client/components/sidebar/sidebar.js | 59 ++++++++++++++++++++++++++++++++++ 2 files changed, 75 insertions(+), 1 deletion(-) (limited to 'client') diff --git a/client/components/sidebar/sidebar.jade b/client/components/sidebar/sidebar.jade index 89622ac1..280eaeaf 100644 --- a/client/components/sidebar/sidebar.jade +++ b/client/components/sidebar/sidebar.jade @@ -302,7 +302,7 @@ template(name="boardMenuPopup") ul.pop-over-list if withApi li - a(href="{{exportUrl}}", download="{{exportFilename}}") + a.js-export-board i.fa.fa-share-alt | {{_ 'export-board'}} li @@ -362,6 +362,21 @@ template(name="boardMenuPopup") i.fa.fa-sitemap | {{_ 'subtask-settings'}} +template(name="exportBoard") + ul.pop-over-list + li + a(href="{{exportUrl}}", download="{{exportJsonFilename}}") + i.fa.fa-share-alt + | {{_ 'export-board-json'}} + li + a(href="{{exportCsvUrl}}", download="{{exportCsvFilename}}") + i.fa.fa-share-alt + | {{_ 'export-board-csv'}} + li + a(href="{{exportTsvUrl}}", download="{{exportTsvFilename}}") + i.fa.fa-share-alt + | {{_ 'export-board-tsv'}} + template(name="labelsWidget") .board-widget.board-widget-labels h3 diff --git a/client/components/sidebar/sidebar.js b/client/components/sidebar/sidebar.js index cbe00797..0541df5e 100644 --- a/client/components/sidebar/sidebar.js +++ b/client/components/sidebar/sidebar.js @@ -1,3 +1,4 @@ +sidebar.js; import { Cookies } from 'meteor/ostrio:cookies'; const cookies = new Cookies(); Sidebar = null; @@ -213,6 +214,7 @@ Template.boardMenuPopup.events({ 'click .js-import-board': Popup.open('chooseBoardSource'), 'click .js-subtask-settings': Popup.open('boardSubtaskSettings'), 'click .js-card-settings': Popup.open('boardCardSettings'), + 'click .js-export-board': Popup.open('exportBoard'), }); Template.boardMenuPopup.onCreated(function() { @@ -405,6 +407,63 @@ BlazeComponent.extendComponent({ }, }).register('chooseBoardSourcePopup'); +BlazeComponent.extendComponent({ + template() { + return 'exportBoard'; + }, + withApi() { + return Template.instance().apiEnabled.get(); + }, + exportUrl() { + const params = { + boardId: Session.get('currentBoard'), + }; + const queryParams = { + authToken: Accounts._storedLoginToken(), + }; + return FlowRouter.path('/api/boards/:boardId/export', params, queryParams); + }, + exportCsvUrl() { + const params = { + boardId: Session.get('currentBoard'), + }; + const queryParams = { + authToken: Accounts._storedLoginToken(), + }; + return FlowRouter.path( + '/api/boards/:boardId/export/csv', + params, + queryParams, + ); + }, + exportTsvUrl() { + const params = { + boardId: Session.get('currentBoard'), + }; + const queryParams = { + authToken: Accounts._storedLoginToken(), + delimiter: '\t', + }; + return FlowRouter.path( + '/api/boards/:boardId/export/csv', + params, + queryParams, + ); + }, + exportJsonFilename() { + const boardId = Session.get('currentBoard'); + return `wekan-export-board-${boardId}.json`; + }, + exportCsvFilename() { + const boardId = Session.get('currentBoard'); + return `wekan-export-board-${boardId}.csv`; + }, + exportTsvFilename() { + const boardId = Session.get('currentBoard'); + return `wekan-export-board-${boardId}.tsv`; + }, +}).register('exportBoardPopup'); + Template.labelsWidget.events({ 'click .js-label': Popup.open('editLabel'), 'click .js-add-label': Popup.open('createLabel'), -- cgit v1.2.3-1-g7c22 From 8a2509007c21a1056f79d183c71cb9f1b3e0857d Mon Sep 17 00:00:00 2001 From: Lauri Ojansivu Date: Wed, 13 May 2020 05:25:04 +0300 Subject: Fix syntax. Maybe sometime later think about translations. Thanks to xet7 ! --- client/components/sidebar/sidebar.js | 1 - 1 file changed, 1 deletion(-) (limited to 'client') diff --git a/client/components/sidebar/sidebar.js b/client/components/sidebar/sidebar.js index 0541df5e..2c1cfd75 100644 --- a/client/components/sidebar/sidebar.js +++ b/client/components/sidebar/sidebar.js @@ -1,4 +1,3 @@ -sidebar.js; import { Cookies } from 'meteor/ostrio:cookies'; const cookies = new Cookies(); Sidebar = null; -- cgit v1.2.3-1-g7c22 From ea0239538a68e225c867411a4f3e0d27c1583837 Mon Sep 17 00:00:00 2001 From: mvolo17 Date: Wed, 13 May 2020 12:44:40 +0200 Subject: Swimlanes ID missing in new boards when creating a new card in a new board there were and error on console. Result: card was not created. adding this parenthesis it works now. Just for info. without this change if you want to create a card you need to change view to swimlines and go back to list view --- client/components/lists/listBody.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'client') diff --git a/client/components/lists/listBody.js b/client/components/lists/listBody.js index 88f88db0..e0b3a66c 100644 --- a/client/components/lists/listBody.js +++ b/client/components/lists/listBody.js @@ -77,7 +77,7 @@ BlazeComponent.extendComponent({ else if ( Utils.boardView() === 'board-view-lists' || Utils.boardView() === 'board-view-cal' || - !Utils.boardView + !Utils.boardView() ) swimlaneId = board.getDefaultSwimline()._id; -- cgit v1.2.3-1-g7c22 From ea74a34d72fb0f33909858a640dbcd3a5fda5b7f Mon Sep 17 00:00:00 2001 From: Lauri Ojansivu Date: Thu, 14 May 2020 01:04:52 +0300 Subject: Add popup and changelog for linked card fixes. --- client/components/cards/cardDetails.js | 13 ++++++++++++- client/components/lists/listHeader.js | 12 +++++++++++- 2 files changed, 23 insertions(+), 2 deletions(-) (limited to 'client') diff --git a/client/components/cards/cardDetails.js b/client/components/cards/cardDetails.js index 048a7e1a..441068b0 100644 --- a/client/components/cards/cardDetails.js +++ b/client/components/cards/cardDetails.js @@ -960,7 +960,18 @@ BlazeComponent.extendComponent({ if (Cards.find({ linkedId: this._id }).count() === 0) { Cards.remove(this._id); } else { - // TODO popup... + // TODO: Maybe later we can list where the linked cards are. + // Now here is popup with a hint that the card cannot be deleted + // as there are linked cards. + // Related: + // client/components/lists/listHeader.js about line 248 + // https://github.com/wekan/wekan/issues/2785 + const message = `${TAPi18n.__( + 'delete-linked-card-before-this-card', + )} linkedId: ${ + this._id + } at client/components/cards/cardDetails.js and https://github.com/wekan/wekan/issues/2785`; + alert(message); } Utils.goBoardId(this.boardId); }), diff --git a/client/components/lists/listHeader.js b/client/components/lists/listHeader.js index a839bb72..7cd4309f 100644 --- a/client/components/lists/listHeader.js +++ b/client/components/lists/listHeader.js @@ -238,9 +238,19 @@ Template.listMorePopup.events({ allCardIds.map(_id => Cards.remove(_id)); Lists.remove(this._id); } else { - // TODO popup with a hint that the list cannot be deleted as there are + // TODO: Figure out more informative message. + // Popup with a hint that the list cannot be deleted as there are // linked cards. We can adapt the query above so we can list the linked // cards. + // Related: + // client/components/cards/cardDetails.js about line 969 + // https://github.com/wekan/wekan/issues/2785 + const message = `${TAPi18n.__( + 'delete-linked-cards-before-this-list', + )} linkedId: ${ + this._id + } at client/components/lists/listHeader.js and https://github.com/wekan/wekan/issues/2785`; + alert(message); } Utils.goBoardId(this.boardId); }), -- cgit v1.2.3-1-g7c22