mirror of
https://github.com/wekan/wekan.git
synced 2025-12-30 14:18:48 +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;
|
||||
}
|
||||
|
||||
/* 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 */
|
||||
.card-details .card-details-header .card-drag-handle {
|
||||
font-size: 20px;
|
||||
|
|
|
|||
|
|
@ -20,10 +20,6 @@ template(name="cardDetails")
|
|||
a.close-card-details.js-close-card-details(title="{{_ 'close-card'}}")
|
||||
| ❌
|
||||
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
|
||||
a.minimize-card-details.js-minimize-card-details(title="{{_ 'minimize-card'}}")
|
||||
| 🔽
|
||||
|
|
|
|||
|
|
@ -324,30 +324,6 @@ BlazeComponent.extendComponent({
|
|||
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) {
|
||||
event.preventDefault();
|
||||
const $card = $(event.target).closest('.card-details');
|
||||
|
|
|
|||
|
|
@ -9,19 +9,10 @@ template(name="checklists")
|
|||
else
|
||||
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
|
||||
each checklist in checklists
|
||||
if checklist.showChecklist card.hideFinishedChecklistIfItemsAreHidden
|
||||
+checklistDetail(checklist = checklist card = card)
|
||||
+checklistDetail(checklist = checklist card = card)
|
||||
|
||||
if canModifyCard
|
||||
+inlinedForm(autoclose=false classNames="js-add-checklist" cardId = cardId)
|
||||
|
|
@ -38,7 +29,7 @@ template(name="checklistDetail")
|
|||
.checklist-title
|
||||
span
|
||||
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
|
||||
h4.title.js-open-inlined-form.is-editable
|
||||
|
|
@ -173,34 +164,62 @@ template(name="checklistActionsPopup")
|
|||
else
|
||||
input.toggle-switch(type="checkbox" id="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")
|
||||
+copyAndMoveChecklist
|
||||
|
||||
template(name="moveChecklistPopup")
|
||||
+copyAndMoveChecklist
|
||||
|
||||
template(name="copyAndMoveChecklist")
|
||||
unless currentUser.isWorker
|
||||
label {{_ 'boards'}}:
|
||||
select.js-select-boards(autofocus)
|
||||
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'}}:
|
||||
select.js-select-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'}}:
|
||||
select.js-select-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
|
||||
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
|
||||
button.primary.confirm.js-done {{_ 'done'}}
|
||||
|
|
|
|||
|
|
@ -230,10 +230,6 @@ BlazeComponent.extendComponent({
|
|||
'focus .js-add-checklist-item': this.focusChecklistItem,
|
||||
// add and delete checklist / checklist-item
|
||||
'click .js-open-inlined-form': this.closeAllInlinedForms,
|
||||
'click #toggleHideFinishedChecklist'(event) {
|
||||
event.preventDefault();
|
||||
this.data().card.toggleHideFinishedChecklist();
|
||||
},
|
||||
keydown: this.pressKey,
|
||||
},
|
||||
];
|
||||
|
|
@ -335,6 +331,12 @@ BlazeComponent.extendComponent({
|
|||
this.data().checklist.toggleHideAllChecklistItems();
|
||||
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) {
|
||||
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');
|
||||
|
||||
|
|
|
|||
|
|
@ -730,3 +730,81 @@
|
|||
align-items: center;
|
||||
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
|
||||
span.badge-icon 📎
|
||||
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
|
||||
.badge
|
||||
span.badge-icon 🌐
|
||||
|
|
@ -181,6 +177,9 @@ template(name="minicard")
|
|||
.badge
|
||||
span.badge-icon 🔢
|
||||
span.badge-text.check-list-sort {{ sort }}
|
||||
if shouldShowChecklistAtMinicard
|
||||
each shouldShowChecklistAtMinicard
|
||||
+minicardChecklist(checklist=. card=..)
|
||||
if currentBoard.allowsDescriptionTextOnMinicard
|
||||
if getDescription
|
||||
.minicard-description
|
||||
|
|
@ -202,55 +201,12 @@ template(name="editCardSortOrderPopup")
|
|||
.edit-controls.clearfix
|
||||
button.primary.confirm.js-submit-edit-card-sort-popup(type="submit") {{_ 'save'}}
|
||||
|
||||
template(name="minicardDetailsActionsPopup")
|
||||
ul.pop-over-list
|
||||
if canModifyCard
|
||||
li
|
||||
a.js-move-card
|
||||
| ➡️
|
||||
| {{_ 'moveCardPopup-title'}}
|
||||
li
|
||||
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'}}
|
||||
template(name="minicardChecklist")
|
||||
.minicard-checklist
|
||||
.checklist-header
|
||||
.checklist-title= checklist.title
|
||||
if canModifyCard
|
||||
a.checklist-menu.js-open-checklist-menu(title="{{_ 'checklistActionsPopup-title'}}") ☰
|
||||
each visibleItems
|
||||
+checklistItemDetail(item = . checklist = checklist card = card)
|
||||
|
||||
|
|
|
|||
|
|
@ -91,6 +91,13 @@ BlazeComponent.extendComponent({
|
|||
}
|
||||
},
|
||||
|
||||
toggleChecklistItem() {
|
||||
const item = this.currentData();
|
||||
if (item && item._id) {
|
||||
item.toggleItem();
|
||||
}
|
||||
},
|
||||
|
||||
events() {
|
||||
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 .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
|
||||
'dragover .minicard'(event) {
|
||||
// Only prevent default for file drags to avoid interfering with sortable
|
||||
|
|
@ -170,6 +177,43 @@ BlazeComponent.extendComponent({
|
|||
},
|
||||
}).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({
|
||||
hiddenMinicardLabelText() {
|
||||
const currentUser = ReactiveCache.getCurrentUser();
|
||||
|
|
@ -209,9 +253,29 @@ Template.minicard.helpers({
|
|||
// Show list name if either:
|
||||
// 1. Board-wide setting is enabled, OR
|
||||
// 2. This specific card has the setting enabled
|
||||
const currentBoard = this.currentBoard;
|
||||
const currentBoard = this.board();
|
||||
if (!currentBoard) return false;
|
||||
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');
|
||||
|
||||
Template.minicardDetailsActionsPopup.events({
|
||||
Template.cardDetailsActionsPopup.events({
|
||||
'click .js-due-date': Popup.open('editCardDueDate'),
|
||||
'click .js-move-card': Popup.open('moveCard'),
|
||||
'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
|
||||
* @param cardId check this card id
|
||||
* @return if the card id was the last confirmed one
|
||||
|
|
|
|||
|
|
@ -1584,6 +1584,7 @@
|
|||
"schedule": "Schedule",
|
||||
"search-boards-or-operations": "Search boards or operations...",
|
||||
"show-list-on-minicard": "Show List on Minicard",
|
||||
"showChecklistAtMinicard": "Show Checklist at Minicard",
|
||||
"showing": "Showing",
|
||||
"start-test-operation": "Start Test Operation",
|
||||
"start-time": "Start Time",
|
||||
|
|
|
|||
|
|
@ -570,6 +570,14 @@ Boards.attachSchema(
|
|||
defaultValue: false,
|
||||
},
|
||||
|
||||
allowsChecklistAtMinicard: {
|
||||
/**
|
||||
* Does the board allow showing checklists on all minicards?
|
||||
*/
|
||||
type: Boolean,
|
||||
defaultValue: false,
|
||||
},
|
||||
|
||||
allowsReceivedDate: {
|
||||
/**
|
||||
* Does the board allows received date?
|
||||
|
|
@ -1578,6 +1586,10 @@ Boards.mutations({
|
|||
return { $set: { allowsShowListsOnMinicard } };
|
||||
},
|
||||
|
||||
setAllowsChecklistAtMinicard(allowsChecklistAtMinicard) {
|
||||
return { $set: { allowsChecklistAtMinicard } };
|
||||
},
|
||||
|
||||
setAllowsRequestedBy(allowsRequestedBy) {
|
||||
return { $set: { allowsRequestedBy } };
|
||||
},
|
||||
|
|
|
|||
|
|
@ -497,13 +497,6 @@ Cards.attachSchema(
|
|||
type: Boolean,
|
||||
defaultValue: false,
|
||||
},
|
||||
hideFinishedChecklistIfItemsAreHidden: {
|
||||
/**
|
||||
* hide completed checklist?
|
||||
*/
|
||||
type: Boolean,
|
||||
optional: true,
|
||||
},
|
||||
showListOnMinicard: {
|
||||
/**
|
||||
* show list name on minicard?
|
||||
|
|
@ -512,6 +505,14 @@ Cards.attachSchema(
|
|||
optional: true,
|
||||
defaultValue: false,
|
||||
},
|
||||
showChecklistAtMinicard: {
|
||||
/**
|
||||
* show checklist on minicard?
|
||||
*/
|
||||
type: Boolean,
|
||||
optional: true,
|
||||
defaultValue: false,
|
||||
},
|
||||
}),
|
||||
);
|
||||
|
||||
|
|
@ -2297,10 +2298,10 @@ Cards.mutations({
|
|||
};
|
||||
},
|
||||
|
||||
toggleHideFinishedChecklist() {
|
||||
toggleShowChecklistAtMinicard() {
|
||||
return {
|
||||
$set: {
|
||||
hideFinishedChecklistIfItemsAreHidden: !this.hideFinishedChecklistIfItemsAreHidden,
|
||||
showChecklistAtMinicard: !this.showChecklistAtMinicard,
|
||||
}
|
||||
};
|
||||
},
|
||||
|
|
|
|||
|
|
@ -77,6 +77,13 @@ Checklists.attachSchema(
|
|||
type: Boolean,
|
||||
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
|
||||
*/
|
||||
move(newCardId) {
|
||||
// update every activity
|
||||
ReactiveCache.getActivities(
|
||||
{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,
|
||||
},
|
||||
});
|
||||
});
|
||||
// Note: Activities and ChecklistItems updates are now handled server-side
|
||||
// in the moveChecklist Meteor method to avoid client-side permission issues
|
||||
|
||||
// update the checklist itself
|
||||
return {
|
||||
$set: {
|
||||
|
|
@ -230,9 +220,69 @@ Checklists.mutations({
|
|||
}
|
||||
};
|
||||
},
|
||||
toggleShowChecklistAtMinicard() {
|
||||
return {
|
||||
$set: {
|
||||
showChecklistAtMinicard: !this.showChecklistAtMinicard,
|
||||
}
|
||||
};
|
||||
},
|
||||
});
|
||||
|
||||
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(() => {
|
||||
Checklists._collection.createIndex({ modifiedAt: -1 });
|
||||
Checklists._collection.createIndex({ cardId: 1, createdAt: 1 });
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue