mirror of
https://github.com/wekan/wekan.git
synced 2026-02-14 04:04:21 +01:00
Opened card Checklist menu: Hide finished tasks. Show Checklist at Minicard.
Thanks to C0rn3j and xet7 ! Fixes #6019, fixes #5567, fixes #2984
This commit is contained in:
parent
cf62807ad5
commit
fbfde81bc8
13 changed files with 312 additions and 161 deletions
|
|
@ -211,25 +211,6 @@ body.desktop-mode .card-details.card-details-collapsed {
|
||||||
color: #000;
|
color: #000;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Bring to front / Send to back buttons */
|
|
||||||
.card-details .card-details-header .card-bring-to-front,
|
|
||||||
.card-details .card-details-header .card-send-to-back {
|
|
||||||
float: right;
|
|
||||||
font-size: 18px;
|
|
||||||
padding: 7px 8px;
|
|
||||||
margin-right: 5px;
|
|
||||||
cursor: pointer;
|
|
||||||
user-select: none;
|
|
||||||
color: #333;
|
|
||||||
}
|
|
||||||
|
|
||||||
.card-details .card-details-header .card-bring-to-front:hover,
|
|
||||||
.card-details .card-details-header .card-send-to-back:hover {
|
|
||||||
color: #000;
|
|
||||||
background: rgba(0,0,0,0.05);
|
|
||||||
border-radius: 3px;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Drag handle */
|
/* Drag handle */
|
||||||
.card-details .card-details-header .card-drag-handle {
|
.card-details .card-details-header .card-drag-handle {
|
||||||
font-size: 20px;
|
font-size: 20px;
|
||||||
|
|
|
||||||
|
|
@ -20,10 +20,6 @@ template(name="cardDetails")
|
||||||
a.close-card-details.js-close-card-details(title="{{_ 'close-card'}}")
|
a.close-card-details.js-close-card-details(title="{{_ 'close-card'}}")
|
||||||
| ❌
|
| ❌
|
||||||
if canModifyCard
|
if canModifyCard
|
||||||
a.card-bring-to-front.js-card-bring-to-front(title="Bring to front")
|
|
||||||
| ⏫
|
|
||||||
a.card-send-to-back.js-card-send-to-back(title="Send to back")
|
|
||||||
| ⏬
|
|
||||||
if cardMaximized
|
if cardMaximized
|
||||||
a.minimize-card-details.js-minimize-card-details(title="{{_ 'minimize-card'}}")
|
a.minimize-card-details.js-minimize-card-details(title="{{_ 'minimize-card'}}")
|
||||||
| 🔽
|
| 🔽
|
||||||
|
|
|
||||||
|
|
@ -324,30 +324,6 @@ BlazeComponent.extendComponent({
|
||||||
Users.setPublicCardCollapsed(!currentState);
|
Users.setPublicCardCollapsed(!currentState);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
'click .js-card-bring-to-front'(event) {
|
|
||||||
event.preventDefault();
|
|
||||||
const $card = $(event.target).closest('.card-details');
|
|
||||||
// Find the highest z-index among all cards
|
|
||||||
let maxZ = 100;
|
|
||||||
$('.card-details').each(function() {
|
|
||||||
const z = parseInt($(this).css('z-index')) || 100;
|
|
||||||
if (z > maxZ) maxZ = z;
|
|
||||||
});
|
|
||||||
// Set this card's z-index to be higher
|
|
||||||
$card.css('z-index', maxZ + 1);
|
|
||||||
},
|
|
||||||
'click .js-card-send-to-back'(event) {
|
|
||||||
event.preventDefault();
|
|
||||||
const $card = $(event.target).closest('.card-details');
|
|
||||||
// Find the lowest z-index among all cards
|
|
||||||
let minZ = 100;
|
|
||||||
$('.card-details').each(function() {
|
|
||||||
const z = parseInt($(this).css('z-index')) || 100;
|
|
||||||
if (z < minZ) minZ = z;
|
|
||||||
});
|
|
||||||
// Set this card's z-index to be lower
|
|
||||||
$card.css('z-index', minZ - 1);
|
|
||||||
},
|
|
||||||
'mousedown .js-card-drag-handle'(event) {
|
'mousedown .js-card-drag-handle'(event) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
const $card = $(event.target).closest('.card-details');
|
const $card = $(event.target).closest('.card-details');
|
||||||
|
|
|
||||||
|
|
@ -9,19 +9,10 @@ template(name="checklists")
|
||||||
else
|
else
|
||||||
a.add-checklist-top.js-open-inlined-form(title="{{_ 'add-checklist'}}")
|
a.add-checklist-top.js-open-inlined-form(title="{{_ 'add-checklist'}}")
|
||||||
| ➕
|
| ➕
|
||||||
if currentUser.isBoardMember
|
|
||||||
.material-toggle-switch(title="{{_ 'hide-finished-checklist'}}")
|
|
||||||
//span.toggle-switch-title
|
|
||||||
if card.hideFinishedChecklistIfItemsAreHidden
|
|
||||||
input.toggle-switch(type="checkbox" id="toggleHideFinishedChecklist" checked="checked")
|
|
||||||
else
|
|
||||||
input.toggle-switch(type="checkbox" id="toggleHideFinishedChecklist")
|
|
||||||
label.toggle-label(for="toggleHideFinishedChecklist")
|
|
||||||
|
|
||||||
.card-checklist-items
|
.card-checklist-items
|
||||||
each checklist in checklists
|
each checklist in checklists
|
||||||
if checklist.showChecklist card.hideFinishedChecklistIfItemsAreHidden
|
+checklistDetail(checklist = checklist card = card)
|
||||||
+checklistDetail(checklist = checklist card = card)
|
|
||||||
|
|
||||||
if canModifyCard
|
if canModifyCard
|
||||||
+inlinedForm(autoclose=false classNames="js-add-checklist" cardId = cardId)
|
+inlinedForm(autoclose=false classNames="js-add-checklist" cardId = cardId)
|
||||||
|
|
@ -38,7 +29,7 @@ template(name="checklistDetail")
|
||||||
.checklist-title
|
.checklist-title
|
||||||
span
|
span
|
||||||
if canModifyCard
|
if canModifyCard
|
||||||
a.checklist-details-menu.js-open-checklist-details-menu(title="{{_ 'checklistActionsPopup-title'}}")
|
a.checklist-details-menu.js-open-checklist-details-menu(title="{{_ 'checklistActionsPopup-title'}}") ☰
|
||||||
|
|
||||||
if canModifyCard
|
if canModifyCard
|
||||||
h4.title.js-open-inlined-form.is-editable
|
h4.title.js-open-inlined-form.is-editable
|
||||||
|
|
@ -173,34 +164,62 @@ template(name="checklistActionsPopup")
|
||||||
else
|
else
|
||||||
input.toggle-switch(type="checkbox" id="toggleHideAllChecklistItems_{{checklist._id}}")
|
input.toggle-switch(type="checkbox" id="toggleHideAllChecklistItems_{{checklist._id}}")
|
||||||
label.toggle-label(for="toggleHideAllChecklistItems_{{checklist._id}}")
|
label.toggle-label(for="toggleHideAllChecklistItems_{{checklist._id}}")
|
||||||
|
a.js-toggle-show-checklist-at-minicard
|
||||||
|
| 📋
|
||||||
|
| {{_ "showChecklistAtMinicard"}} ...
|
||||||
|
.material-toggle-switch(title="{{_ 'showChecklistAtMinicard'}}")
|
||||||
|
if checklist.showChecklistAtMinicard
|
||||||
|
input.toggle-switch(type="checkbox" id="toggleShowChecklistAtMinicard_{{checklist._id}}" checked="checked")
|
||||||
|
else
|
||||||
|
input.toggle-switch(type="checkbox" id="toggleShowChecklistAtMinicard_{{checklist._id}}")
|
||||||
|
label.toggle-label(for="toggleShowChecklistAtMinicard_{{checklist._id}}")
|
||||||
|
|
||||||
template(name="copyChecklistPopup")
|
template(name="copyChecklistPopup")
|
||||||
+copyAndMoveChecklist
|
|
||||||
|
|
||||||
template(name="moveChecklistPopup")
|
|
||||||
+copyAndMoveChecklist
|
|
||||||
|
|
||||||
template(name="copyAndMoveChecklist")
|
|
||||||
unless currentUser.isWorker
|
unless currentUser.isWorker
|
||||||
label {{_ 'boards'}}:
|
label {{_ 'boards'}}:
|
||||||
select.js-select-boards(autofocus)
|
select.js-select-boards(autofocus)
|
||||||
each boards
|
each boards
|
||||||
option(value="{{_id}}" selected="{{#if isDialogOptionBoardId _id}}selected{{/if}}") {{title}}
|
option(value="{{_id}}" selected="{{#if isDialogOptionBoardId _id}}selected{{/if}}") {{add @index 1}}. {{title}}
|
||||||
|
|
||||||
label {{_ 'swimlanes'}}:
|
label {{_ 'swimlanes'}}:
|
||||||
select.js-select-swimlanes
|
select.js-select-swimlanes
|
||||||
each swimlanes
|
each swimlanes
|
||||||
option(value="{{_id}}" selected="{{#if isDialogOptionSwimlaneId _id}}selected{{/if}}") {{title}}
|
option(value="{{_id}}" selected="{{#if isDialogOptionSwimlaneId _id}}selected{{/if}}") {{add @index 1}}. {{title}}
|
||||||
|
|
||||||
label {{_ 'lists'}}:
|
label {{_ 'lists'}}:
|
||||||
select.js-select-lists
|
select.js-select-lists
|
||||||
each lists
|
each lists
|
||||||
option(value="{{_id}}" selected="{{#if isDialogOptionListId _id}}selected{{/if}}") {{title}}
|
option(value="{{_id}}" selected="{{#if isDialogOptionListId _id}}selected{{/if}}") {{add @index 1}}. {{title}}
|
||||||
|
|
||||||
label {{_ 'cards'}}:
|
label {{_ 'card'}}:
|
||||||
select.js-select-cards
|
select.js-select-cards
|
||||||
each cards
|
each cards
|
||||||
option(value="{{_id}}" selected="{{#if isDialogOptionCardId _id}}selected{{/if}}") {{title}}
|
option(value="{{_id}}" selected="{{#if isDialogOptionCardId _id}}selected{{/if}}") {{add @index 1}}. {{title}}
|
||||||
|
|
||||||
|
.edit-controls.clearfix
|
||||||
|
button.primary.confirm.js-done {{_ 'done'}}
|
||||||
|
|
||||||
|
template(name="moveChecklistPopup")
|
||||||
|
unless currentUser.isWorker
|
||||||
|
label {{_ 'boards'}}:
|
||||||
|
select.js-select-boards(autofocus)
|
||||||
|
each boards
|
||||||
|
option(value="{{_id}}" selected="{{#if isDialogOptionBoardId _id}}selected{{/if}}") {{add @index 1}}. {{title}}
|
||||||
|
|
||||||
|
label {{_ 'swimlanes'}}:
|
||||||
|
select.js-select-swimlanes
|
||||||
|
each swimlanes
|
||||||
|
option(value="{{_id}}" selected="{{#if isDialogOptionSwimlaneId _id}}selected{{/if}}") {{add @index 1}}. {{title}}
|
||||||
|
|
||||||
|
label {{_ 'lists'}}:
|
||||||
|
select.js-select-lists
|
||||||
|
each lists
|
||||||
|
option(value="{{_id}}" selected="{{#if isDialogOptionListId _id}}selected{{/if}}") {{add @index 1}}. {{title}}
|
||||||
|
|
||||||
|
label {{_ 'card'}}:
|
||||||
|
select.js-select-cards
|
||||||
|
each cards
|
||||||
|
option(value="{{_id}}" selected="{{#if isDialogOptionCardId _id}}selected{{/if}}") {{add @index 1}}. {{title}}
|
||||||
|
|
||||||
.edit-controls.clearfix
|
.edit-controls.clearfix
|
||||||
button.primary.confirm.js-done {{_ 'done'}}
|
button.primary.confirm.js-done {{_ 'done'}}
|
||||||
|
|
|
||||||
|
|
@ -230,10 +230,6 @@ BlazeComponent.extendComponent({
|
||||||
'focus .js-add-checklist-item': this.focusChecklistItem,
|
'focus .js-add-checklist-item': this.focusChecklistItem,
|
||||||
// add and delete checklist / checklist-item
|
// add and delete checklist / checklist-item
|
||||||
'click .js-open-inlined-form': this.closeAllInlinedForms,
|
'click .js-open-inlined-form': this.closeAllInlinedForms,
|
||||||
'click #toggleHideFinishedChecklist'(event) {
|
|
||||||
event.preventDefault();
|
|
||||||
this.data().card.toggleHideFinishedChecklist();
|
|
||||||
},
|
|
||||||
keydown: this.pressKey,
|
keydown: this.pressKey,
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
@ -335,6 +331,12 @@ BlazeComponent.extendComponent({
|
||||||
this.data().checklist.toggleHideAllChecklistItems();
|
this.data().checklist.toggleHideAllChecklistItems();
|
||||||
Popup.back();
|
Popup.back();
|
||||||
},
|
},
|
||||||
|
'click .js-toggle-show-checklist-at-minicard'(event) {
|
||||||
|
event.preventDefault();
|
||||||
|
const checklist = this.data().checklist;
|
||||||
|
checklist.toggleShowChecklistAtMinicard();
|
||||||
|
Popup.back();
|
||||||
|
},
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
@ -388,7 +390,12 @@ BlazeComponent.extendComponent({
|
||||||
}
|
}
|
||||||
setDone(cardId, options) {
|
setDone(cardId, options) {
|
||||||
ReactiveCache.getCurrentUser().setMoveChecklistDialogOption(this.currentBoardId, options);
|
ReactiveCache.getCurrentUser().setMoveChecklistDialogOption(this.currentBoardId, options);
|
||||||
this.data().checklist.move(cardId);
|
const checklist = this.data().checklist;
|
||||||
|
Meteor.call('moveChecklist', checklist._id, cardId, (error) => {
|
||||||
|
if (error) {
|
||||||
|
console.error('Error moving checklist:', error);
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}).register('moveChecklistPopup');
|
}).register('moveChecklistPopup');
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -730,3 +730,81 @@
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 0.3vw;
|
gap: 0.3vw;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Checklist display on minicard */
|
||||||
|
.minicard-checklist {
|
||||||
|
width: 100%;
|
||||||
|
margin-top: 0.5vh;
|
||||||
|
margin-bottom: 0.5vh;
|
||||||
|
padding: 0.3vh 0.5vw;
|
||||||
|
background-color: rgba(255, 255, 255, 0.8);
|
||||||
|
border-radius: 0.3vw;
|
||||||
|
border: 1px solid #e0e0e0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.minicard-checklist .checklist-header {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
margin-bottom: 0.3vh;
|
||||||
|
}
|
||||||
|
|
||||||
|
.minicard-checklist .checklist-title {
|
||||||
|
font-size: 0.8em;
|
||||||
|
font-weight: bold;
|
||||||
|
color: #4d4d4d;
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.minicard-checklist .checklist-menu {
|
||||||
|
font-size: 1.2em;
|
||||||
|
color: #666;
|
||||||
|
cursor: pointer;
|
||||||
|
padding: 0 0.3vw;
|
||||||
|
border-radius: 0.2vw;
|
||||||
|
transition: background-color 0.2s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.minicard-checklist .checklist-menu:hover {
|
||||||
|
background-color: rgba(0, 0, 0, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.minicard-checklist .checklist-item {
|
||||||
|
font-size: 0.75em;
|
||||||
|
color: #666;
|
||||||
|
margin-bottom: 0.2vh;
|
||||||
|
display: flex;
|
||||||
|
align-items: flex-start;
|
||||||
|
gap: 0.3vw;
|
||||||
|
line-height: 1.2;
|
||||||
|
cursor: pointer;
|
||||||
|
padding: 0.2vh 0;
|
||||||
|
border-radius: 0.2vw;
|
||||||
|
transition: background-color 0.2s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.minicard-checklist .checklist-item:hover {
|
||||||
|
background-color: rgba(0, 0, 0, 0.05);
|
||||||
|
}
|
||||||
|
|
||||||
|
.minicard-checklist .checklist-item.is-checked {
|
||||||
|
text-decoration: line-through;
|
||||||
|
color: #999;
|
||||||
|
}
|
||||||
|
|
||||||
|
.minicard-checklist .checklist-item .check-box-unicode {
|
||||||
|
flex-shrink: 0;
|
||||||
|
font-size: 0.8em;
|
||||||
|
margin-top: 0.1vh;
|
||||||
|
transition: transform 0.2s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.minicard-checklist .checklist-item:hover .check-box-unicode {
|
||||||
|
transform: scale(1.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.minicard-checklist .checklist-item .item-title {
|
||||||
|
flex: 1;
|
||||||
|
word-wrap: break-word;
|
||||||
|
overflow-wrap: break-word;
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -167,10 +167,6 @@ template(name="minicard")
|
||||||
.badge
|
.badge
|
||||||
span.badge-icon 📎
|
span.badge-icon 📎
|
||||||
span.badge-text= attachments.length
|
span.badge-text= attachments.length
|
||||||
if checklists.length
|
|
||||||
.badge(class="{{#if checklistFinished}}is-finished{{/if}}")
|
|
||||||
span.badge-icon ☑️
|
|
||||||
span.badge-text.check-list-text {{checklistFinishedCount}}/{{checklistItemCount}}
|
|
||||||
if allSubtasks.count
|
if allSubtasks.count
|
||||||
.badge
|
.badge
|
||||||
span.badge-icon 🌐
|
span.badge-icon 🌐
|
||||||
|
|
@ -181,6 +177,9 @@ template(name="minicard")
|
||||||
.badge
|
.badge
|
||||||
span.badge-icon 🔢
|
span.badge-icon 🔢
|
||||||
span.badge-text.check-list-sort {{ sort }}
|
span.badge-text.check-list-sort {{ sort }}
|
||||||
|
if shouldShowChecklistAtMinicard
|
||||||
|
each shouldShowChecklistAtMinicard
|
||||||
|
+minicardChecklist(checklist=. card=..)
|
||||||
if currentBoard.allowsDescriptionTextOnMinicard
|
if currentBoard.allowsDescriptionTextOnMinicard
|
||||||
if getDescription
|
if getDescription
|
||||||
.minicard-description
|
.minicard-description
|
||||||
|
|
@ -202,55 +201,12 @@ template(name="editCardSortOrderPopup")
|
||||||
.edit-controls.clearfix
|
.edit-controls.clearfix
|
||||||
button.primary.confirm.js-submit-edit-card-sort-popup(type="submit") {{_ 'save'}}
|
button.primary.confirm.js-submit-edit-card-sort-popup(type="submit") {{_ 'save'}}
|
||||||
|
|
||||||
template(name="minicardDetailsActionsPopup")
|
template(name="minicardChecklist")
|
||||||
ul.pop-over-list
|
.minicard-checklist
|
||||||
if canModifyCard
|
.checklist-header
|
||||||
li
|
.checklist-title= checklist.title
|
||||||
a.js-move-card
|
if canModifyCard
|
||||||
| ➡️
|
a.checklist-menu.js-open-checklist-menu(title="{{_ 'checklistActionsPopup-title'}}") ☰
|
||||||
| {{_ 'moveCardPopup-title'}}
|
each visibleItems
|
||||||
li
|
+checklistItemDetail(item = . checklist = checklist card = card)
|
||||||
a.js-copy-card
|
|
||||||
| 📋
|
|
||||||
| {{_ 'copyCardPopup-title'}}
|
|
||||||
hr
|
|
||||||
li
|
|
||||||
a.js-archive
|
|
||||||
| ➡️
|
|
||||||
| 📦
|
|
||||||
| {{_ 'archive-card'}}
|
|
||||||
hr
|
|
||||||
li
|
|
||||||
a.js-move-card-to-top
|
|
||||||
| ⬆️
|
|
||||||
| {{_ 'moveCardToTop-title'}}
|
|
||||||
li
|
|
||||||
a.js-move-card-to-bottom
|
|
||||||
| ⬇️
|
|
||||||
| {{_ 'moveCardToBottom-title'}}
|
|
||||||
hr
|
|
||||||
li
|
|
||||||
a.js-add-labels
|
|
||||||
| 🏷️
|
|
||||||
| {{_ 'card-edit-labels'}}
|
|
||||||
li
|
|
||||||
a.js-due-date
|
|
||||||
| 📥
|
|
||||||
| {{_ 'editCardDueDatePopup-title'}}
|
|
||||||
li
|
|
||||||
a.js-set-card-color
|
|
||||||
| 🎨
|
|
||||||
| {{_ 'setCardColorPopup-title'}}
|
|
||||||
li
|
|
||||||
a.js-link
|
|
||||||
| 🔗
|
|
||||||
| {{_ 'link-card'}}
|
|
||||||
li
|
|
||||||
a.js-toggle-watch-card
|
|
||||||
if isWatching
|
|
||||||
| 👁️
|
|
||||||
| {{_ 'unwatch'}}
|
|
||||||
else
|
|
||||||
| 👁️
|
|
||||||
| {{_ 'watch'}}
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -91,6 +91,13 @@ BlazeComponent.extendComponent({
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
toggleChecklistItem() {
|
||||||
|
const item = this.currentData();
|
||||||
|
if (item && item._id) {
|
||||||
|
item.toggleItem();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
events() {
|
events() {
|
||||||
return [
|
return [
|
||||||
{
|
{
|
||||||
|
|
@ -108,7 +115,7 @@ BlazeComponent.extendComponent({
|
||||||
},
|
},
|
||||||
'click span.badge-icon.fa.fa-sort, click span.badge-text.check-list-sort' : Popup.open("editCardSortOrder"),
|
'click span.badge-icon.fa.fa-sort, click span.badge-text.check-list-sort' : Popup.open("editCardSortOrder"),
|
||||||
'click .minicard-labels' : this.cardLabelsPopup,
|
'click .minicard-labels' : this.cardLabelsPopup,
|
||||||
'click .js-open-minicard-details-menu': Popup.open('minicardDetailsActions'),
|
'click .js-open-minicard-details-menu': Popup.open('cardDetailsActions'),
|
||||||
// Drag and drop file upload handlers
|
// Drag and drop file upload handlers
|
||||||
'dragover .minicard'(event) {
|
'dragover .minicard'(event) {
|
||||||
// Only prevent default for file drags to avoid interfering with sortable
|
// Only prevent default for file drags to avoid interfering with sortable
|
||||||
|
|
@ -170,6 +177,43 @@ BlazeComponent.extendComponent({
|
||||||
},
|
},
|
||||||
}).register('minicard');
|
}).register('minicard');
|
||||||
|
|
||||||
|
BlazeComponent.extendComponent({
|
||||||
|
template() {
|
||||||
|
return 'minicardChecklist';
|
||||||
|
},
|
||||||
|
|
||||||
|
events() {
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
'click .js-open-checklist-menu'(event) {
|
||||||
|
const data = this.currentData();
|
||||||
|
const checklist = data.checklist || data;
|
||||||
|
const card = data.card || this.data();
|
||||||
|
const context = { currentData: () => ({ checklist, card }) };
|
||||||
|
Popup.open('checklistActions').call(context, event);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
];
|
||||||
|
},
|
||||||
|
|
||||||
|
visibleItems() {
|
||||||
|
const checklist = this.currentData().checklist || this.currentData();
|
||||||
|
const items = checklist.items();
|
||||||
|
|
||||||
|
return items.filter(item => {
|
||||||
|
// Hide finished items if hideCheckedChecklistItems is true
|
||||||
|
if (item.isFinished && checklist.hideCheckedChecklistItems) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// Hide all items if hideAllChecklistItems is true
|
||||||
|
if (checklist.hideAllChecklistItems) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
}).register('minicardChecklist');
|
||||||
|
|
||||||
Template.minicard.helpers({
|
Template.minicard.helpers({
|
||||||
hiddenMinicardLabelText() {
|
hiddenMinicardLabelText() {
|
||||||
const currentUser = ReactiveCache.getCurrentUser();
|
const currentUser = ReactiveCache.getCurrentUser();
|
||||||
|
|
@ -209,9 +253,29 @@ Template.minicard.helpers({
|
||||||
// Show list name if either:
|
// Show list name if either:
|
||||||
// 1. Board-wide setting is enabled, OR
|
// 1. Board-wide setting is enabled, OR
|
||||||
// 2. This specific card has the setting enabled
|
// 2. This specific card has the setting enabled
|
||||||
const currentBoard = this.currentBoard;
|
const currentBoard = this.board();
|
||||||
if (!currentBoard) return false;
|
if (!currentBoard) return false;
|
||||||
return currentBoard.allowsShowListsOnMinicard || this.showListOnMinicard;
|
return currentBoard.allowsShowListsOnMinicard || this.showListOnMinicard;
|
||||||
|
},
|
||||||
|
|
||||||
|
shouldShowChecklistAtMinicard() {
|
||||||
|
// Return checklists that should be shown on minicard
|
||||||
|
const currentBoard = this.board();
|
||||||
|
if (!currentBoard) return [];
|
||||||
|
|
||||||
|
const checklists = this.checklists();
|
||||||
|
const visibleChecklists = [];
|
||||||
|
|
||||||
|
checklists.forEach(checklist => {
|
||||||
|
// Show checklist if either:
|
||||||
|
// 1. Board-wide setting is enabled, OR
|
||||||
|
// 2. This specific checklist has the setting enabled
|
||||||
|
if (currentBoard.allowsChecklistAtMinicard || checklist.showChecklistAtMinicard) {
|
||||||
|
visibleChecklists.push(checklist);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return visibleChecklists;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -242,7 +306,7 @@ BlazeComponent.extendComponent({
|
||||||
}
|
}
|
||||||
}).register('editCardSortOrderPopup');
|
}).register('editCardSortOrderPopup');
|
||||||
|
|
||||||
Template.minicardDetailsActionsPopup.events({
|
Template.cardDetailsActionsPopup.events({
|
||||||
'click .js-due-date': Popup.open('editCardDueDate'),
|
'click .js-due-date': Popup.open('editCardDueDate'),
|
||||||
'click .js-move-card': Popup.open('moveCard'),
|
'click .js-move-card': Popup.open('moveCard'),
|
||||||
'click .js-copy-card': Popup.open('copyCard'),
|
'click .js-copy-card': Popup.open('copyCard'),
|
||||||
|
|
|
||||||
|
|
@ -29,6 +29,16 @@ export class DialogWithBoardSwimlaneListCard extends DialogWithBoardSwimlaneList
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** returns all available cards of the current list */
|
||||||
|
cards() {
|
||||||
|
const list = ReactiveCache.getList({_id: this.selectedListId.get(), boardId: this.selectedBoardId.get()});
|
||||||
|
if (list) {
|
||||||
|
return list.cards();
|
||||||
|
} else {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/** returns if the card id was the last confirmed one
|
/** returns if the card id was the last confirmed one
|
||||||
* @param cardId check this card id
|
* @param cardId check this card id
|
||||||
* @return if the card id was the last confirmed one
|
* @return if the card id was the last confirmed one
|
||||||
|
|
|
||||||
|
|
@ -1584,6 +1584,7 @@
|
||||||
"schedule": "Schedule",
|
"schedule": "Schedule",
|
||||||
"search-boards-or-operations": "Search boards or operations...",
|
"search-boards-or-operations": "Search boards or operations...",
|
||||||
"show-list-on-minicard": "Show List on Minicard",
|
"show-list-on-minicard": "Show List on Minicard",
|
||||||
|
"showChecklistAtMinicard": "Show Checklist at Minicard",
|
||||||
"showing": "Showing",
|
"showing": "Showing",
|
||||||
"start-test-operation": "Start Test Operation",
|
"start-test-operation": "Start Test Operation",
|
||||||
"start-time": "Start Time",
|
"start-time": "Start Time",
|
||||||
|
|
|
||||||
|
|
@ -570,6 +570,14 @@ Boards.attachSchema(
|
||||||
defaultValue: false,
|
defaultValue: false,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
allowsChecklistAtMinicard: {
|
||||||
|
/**
|
||||||
|
* Does the board allow showing checklists on all minicards?
|
||||||
|
*/
|
||||||
|
type: Boolean,
|
||||||
|
defaultValue: false,
|
||||||
|
},
|
||||||
|
|
||||||
allowsReceivedDate: {
|
allowsReceivedDate: {
|
||||||
/**
|
/**
|
||||||
* Does the board allows received date?
|
* Does the board allows received date?
|
||||||
|
|
@ -1578,6 +1586,10 @@ Boards.mutations({
|
||||||
return { $set: { allowsShowListsOnMinicard } };
|
return { $set: { allowsShowListsOnMinicard } };
|
||||||
},
|
},
|
||||||
|
|
||||||
|
setAllowsChecklistAtMinicard(allowsChecklistAtMinicard) {
|
||||||
|
return { $set: { allowsChecklistAtMinicard } };
|
||||||
|
},
|
||||||
|
|
||||||
setAllowsRequestedBy(allowsRequestedBy) {
|
setAllowsRequestedBy(allowsRequestedBy) {
|
||||||
return { $set: { allowsRequestedBy } };
|
return { $set: { allowsRequestedBy } };
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -497,13 +497,6 @@ Cards.attachSchema(
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
defaultValue: false,
|
defaultValue: false,
|
||||||
},
|
},
|
||||||
hideFinishedChecklistIfItemsAreHidden: {
|
|
||||||
/**
|
|
||||||
* hide completed checklist?
|
|
||||||
*/
|
|
||||||
type: Boolean,
|
|
||||||
optional: true,
|
|
||||||
},
|
|
||||||
showListOnMinicard: {
|
showListOnMinicard: {
|
||||||
/**
|
/**
|
||||||
* show list name on minicard?
|
* show list name on minicard?
|
||||||
|
|
@ -512,6 +505,14 @@ Cards.attachSchema(
|
||||||
optional: true,
|
optional: true,
|
||||||
defaultValue: false,
|
defaultValue: false,
|
||||||
},
|
},
|
||||||
|
showChecklistAtMinicard: {
|
||||||
|
/**
|
||||||
|
* show checklist on minicard?
|
||||||
|
*/
|
||||||
|
type: Boolean,
|
||||||
|
optional: true,
|
||||||
|
defaultValue: false,
|
||||||
|
},
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
@ -2297,10 +2298,10 @@ Cards.mutations({
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
toggleHideFinishedChecklist() {
|
toggleShowChecklistAtMinicard() {
|
||||||
return {
|
return {
|
||||||
$set: {
|
$set: {
|
||||||
hideFinishedChecklistIfItemsAreHidden: !this.hideFinishedChecklistIfItemsAreHidden,
|
showChecklistAtMinicard: !this.showChecklistAtMinicard,
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -77,6 +77,13 @@ Checklists.attachSchema(
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
optional: true,
|
optional: true,
|
||||||
},
|
},
|
||||||
|
showChecklistAtMinicard: {
|
||||||
|
/**
|
||||||
|
* show this checklist on minicard?
|
||||||
|
*/
|
||||||
|
type: Boolean,
|
||||||
|
defaultValue: false,
|
||||||
|
},
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
@ -189,26 +196,9 @@ Checklists.mutations({
|
||||||
* @param newCardId move the checklist to this cardId
|
* @param newCardId move the checklist to this cardId
|
||||||
*/
|
*/
|
||||||
move(newCardId) {
|
move(newCardId) {
|
||||||
// update every activity
|
// Note: Activities and ChecklistItems updates are now handled server-side
|
||||||
ReactiveCache.getActivities(
|
// in the moveChecklist Meteor method to avoid client-side permission issues
|
||||||
{checklistId: this._id}
|
|
||||||
).forEach(activity => {
|
|
||||||
Activities.update(activity._id, {
|
|
||||||
$set: {
|
|
||||||
cardId: newCardId,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
});
|
|
||||||
// update every checklist-item
|
|
||||||
ReactiveCache.getChecklistItems(
|
|
||||||
{checklistId: this._id}
|
|
||||||
).forEach(checklistItem => {
|
|
||||||
ChecklistItems.update(checklistItem._id, {
|
|
||||||
$set: {
|
|
||||||
cardId: newCardId,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
});
|
|
||||||
// update the checklist itself
|
// update the checklist itself
|
||||||
return {
|
return {
|
||||||
$set: {
|
$set: {
|
||||||
|
|
@ -230,9 +220,69 @@ Checklists.mutations({
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
toggleShowChecklistAtMinicard() {
|
||||||
|
return {
|
||||||
|
$set: {
|
||||||
|
showChecklistAtMinicard: !this.showChecklistAtMinicard,
|
||||||
|
}
|
||||||
|
};
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
if (Meteor.isServer) {
|
if (Meteor.isServer) {
|
||||||
|
Meteor.methods({
|
||||||
|
moveChecklist(checklistId, newCardId) {
|
||||||
|
check(checklistId, String);
|
||||||
|
check(newCardId, String);
|
||||||
|
|
||||||
|
const checklist = ReactiveCache.getChecklist(checklistId);
|
||||||
|
if (!checklist) {
|
||||||
|
throw new Meteor.Error('checklist-not-found', 'Checklist not found');
|
||||||
|
}
|
||||||
|
|
||||||
|
const newCard = ReactiveCache.getCard(newCardId);
|
||||||
|
if (!newCard) {
|
||||||
|
throw new Meteor.Error('card-not-found', 'Target card not found');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check permissions on both source and target cards
|
||||||
|
const sourceCard = ReactiveCache.getCard(checklist.cardId);
|
||||||
|
if (!allowIsBoardMemberByCard(this.userId, sourceCard)) {
|
||||||
|
throw new Meteor.Error('not-authorized', 'Not authorized to move checklist from source card');
|
||||||
|
}
|
||||||
|
if (!allowIsBoardMemberByCard(this.userId, newCard)) {
|
||||||
|
throw new Meteor.Error('not-authorized', 'Not authorized to move checklist to target card');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update activities
|
||||||
|
ReactiveCache.getActivities({ checklistId }).forEach(activity => {
|
||||||
|
Activities.update(activity._id, {
|
||||||
|
$set: {
|
||||||
|
cardId: newCardId,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// Update checklist items
|
||||||
|
ReactiveCache.getChecklistItems({ checklistId }).forEach(checklistItem => {
|
||||||
|
ChecklistItems.update(checklistItem._id, {
|
||||||
|
$set: {
|
||||||
|
cardId: newCardId,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// Update the checklist itself
|
||||||
|
Checklists.update(checklistId, {
|
||||||
|
$set: {
|
||||||
|
cardId: newCardId,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
return checklistId;
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
Meteor.startup(() => {
|
Meteor.startup(() => {
|
||||||
Checklists._collection.createIndex({ modifiedAt: -1 });
|
Checklists._collection.createIndex({ modifiedAt: -1 });
|
||||||
Checklists._collection.createIndex({ cardId: 1, createdAt: 1 });
|
Checklists._collection.createIndex({ cardId: 1, createdAt: 1 });
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue