Fix Regression - unable to view cards by due date v8.11.

Thanks to xet7 !

Fixes #5964
This commit is contained in:
Lauri Ojansivu 2025-10-22 23:31:36 +03:00
parent 8e296231ba
commit ae11e80bde
6 changed files with 184 additions and 27 deletions

View file

@ -187,7 +187,7 @@ BlazeComponent.extendComponent({
console.log(`Board ${boardId} has no shared lists to convert`); console.log(`Board ${boardId} has no shared lists to convert`);
} }
// Mark as processed even if no shared lists // Mark as processed even if no shared lists
Meteor.call('boards.update', boardId, { $set: { hasSharedListsConverted: true } }); Boards.update(boardId, { $set: { hasSharedListsConverted: true } });
return; return;
} }
@ -259,7 +259,7 @@ BlazeComponent.extendComponent({
} }
// Mark board as processed // Mark board as processed
Meteor.call('boards.update', boardId, { $set: { hasSharedListsConverted: true } }); Boards.update(boardId, { $set: { hasSharedListsConverted: true } });
if (process.env.DEBUG === 'true') { if (process.env.DEBUG === 'true') {
console.log(`Successfully converted ${sharedLists.length} shared lists to per-swimlane lists for board ${boardId}`); console.log(`Successfully converted ${sharedLists.length} shared lists to per-swimlane lists for board ${boardId}`);
@ -361,14 +361,14 @@ BlazeComponent.extendComponent({
} }
// Mark board as processed // Mark board as processed
Meteor.call('boards.update', boardId, { $set: { fixDuplicateListsCompleted: true } }); Boards.update(boardId, { $set: { fixDuplicateListsCompleted: true } });
} else if (process.env.DEBUG === 'true') { } else if (process.env.DEBUG === 'true') {
console.log(`No duplicate lists found for board ${boardId}`); console.log(`No duplicate lists found for board ${boardId}`);
// Still mark as processed to avoid repeated checks // Still mark as processed to avoid repeated checks
Meteor.call('boards.update', boardId, { $set: { fixDuplicateListsCompleted: true } }); Boards.update(boardId, { $set: { fixDuplicateListsCompleted: true } });
} else { } else {
// Still mark as processed to avoid repeated checks // Still mark as processed to avoid repeated checks
Meteor.call('boards.update', boardId, { $set: { fixDuplicateListsCompleted: true } }); Boards.update(boardId, { $set: { fixDuplicateListsCompleted: true } });
} }
} catch (error) { } catch (error) {

View file

@ -32,8 +32,16 @@ template(name="dueCards")
span.global-search-error-messages span.global-search-error-messages
= msg = msg
else else
.due-cards-results-header
h1
= resultsText
each card in dueCardsList each card in dueCardsList
+resultCard(card) +resultCard(card)
else
.global-search-results-list-wrapper
.no-results
h3 {{_ 'dueCards-noResults-title'}}
p {{_ 'dueCards-noResults-description'}}
template(name="dueCardsViewChangePopup") template(name="dueCardsViewChangePopup")
if currentUser if currentUser

View file

@ -1,5 +1,6 @@
import { ReactiveCache } from '/imports/reactiveCache'; import { ReactiveCache } from '/imports/reactiveCache';
import { BlazeComponent } from 'meteor/peerlibrary:blaze-components'; import { BlazeComponent } from 'meteor/peerlibrary:blaze-components';
import { TAPi18n } from '/imports/i18n';
// const subManager = new SubsManager(); // const subManager = new SubsManager();
@ -24,22 +25,25 @@ Template.dueCards.helpers({
return Meteor.userId(); return Meteor.userId();
}, },
dueCardsList() { dueCardsList() {
const component = BlazeComponent.getComponentForElement(this); const component = BlazeComponent.getComponentForElement(this.firstNode);
if (component && component.dueCardsList) { if (component && component.dueCardsList) {
return component.dueCardsList(); return component.dueCardsList();
} }
return []; return [];
}, },
hasResults() { hasResults() {
const component = BlazeComponent.getComponentForElement(this); const component = BlazeComponent.getComponentForElement(this.firstNode);
if (component && component.dueCardsList) { if (component && component.hasResults) {
const cards = component.dueCardsList(); return component.hasResults.get();
return cards && cards.length > 0;
} }
return false; return false;
}, },
searching() { searching() {
return false; // No longer using search, so always false const component = BlazeComponent.getComponentForElement(this.firstNode);
if (component && component.isLoading) {
return component.isLoading.get();
}
return true; // Show loading by default
}, },
hasQueryErrors() { hasQueryErrors() {
return false; // No longer using search, so always false return false; // No longer using search, so always false
@ -47,6 +51,20 @@ Template.dueCards.helpers({
errorMessages() { errorMessages() {
return []; // No longer using search, so always empty return []; // No longer using search, so always empty
}, },
cardsCount() {
const component = BlazeComponent.getComponentForElement(this.firstNode);
if (component && component.cardsCount) {
return component.cardsCount();
}
return 0;
},
resultsText() {
const component = BlazeComponent.getComponentForElement(this.firstNode);
if (component && component.resultsText) {
return component.resultsText();
}
return '';
},
}); });
BlazeComponent.extendComponent({ BlazeComponent.extendComponent({
@ -78,6 +96,9 @@ class DueCardsComponent extends BlazeComponent {
this._cachedCards = null; this._cachedCards = null;
this._cachedTimestamp = null; this._cachedTimestamp = null;
this.subscriptionHandle = null; this.subscriptionHandle = null;
this.isLoading = new ReactiveVar(true);
this.hasResults = new ReactiveVar(false);
this.searching = new ReactiveVar(false);
// Subscribe to the optimized due cards publication // Subscribe to the optimized due cards publication
this.autorun(() => { this.autorun(() => {
@ -86,6 +107,24 @@ class DueCardsComponent extends BlazeComponent {
this.subscriptionHandle.stop(); this.subscriptionHandle.stop();
} }
this.subscriptionHandle = Meteor.subscribe('dueCards', allUsers); this.subscriptionHandle = Meteor.subscribe('dueCards', allUsers);
// Update loading state based on subscription
this.autorun(() => {
if (this.subscriptionHandle && this.subscriptionHandle.ready()) {
if (process.env.DEBUG === 'true') {
console.log('dueCards: subscription ready, loading data...');
}
this.isLoading.set(false);
const cards = this.dueCardsList();
this.hasResults.set(cards && cards.length > 0);
} else {
if (process.env.DEBUG === 'true') {
console.log('dueCards: subscription not ready, showing loading...');
}
this.isLoading.set(true);
this.hasResults.set(false);
}
});
}); });
} }
@ -106,9 +145,45 @@ class DueCardsComponent extends BlazeComponent {
return this.dueCardsView() === 'board'; return this.dueCardsView() === 'board';
} }
hasResults() {
return this.hasResults.get();
}
cardsCount() {
const cards = this.dueCardsList();
return cards ? cards.length : 0;
}
resultsText() {
const count = this.cardsCount();
if (count === 1) {
return TAPi18n.__('one-card-found');
} else {
// Get the translated text and manually replace %s with the count
const baseText = TAPi18n.__('n-cards-found');
const result = baseText.replace('%s', count);
if (process.env.DEBUG === 'true') {
console.log('dueCards: base text:', baseText, 'count:', count, 'result:', result);
}
return result;
}
}
dueCardsList() { dueCardsList() {
// Check if subscription is ready
if (!this.subscriptionHandle || !this.subscriptionHandle.ready()) {
if (process.env.DEBUG === 'true') {
console.log('dueCards client: subscription not ready');
}
return [];
}
// Use cached results if available to avoid expensive re-sorting // Use cached results if available to avoid expensive re-sorting
if (this._cachedCards && this._cachedTimestamp && (Date.now() - this._cachedTimestamp < 5000)) { if (this._cachedCards && this._cachedTimestamp && (Date.now() - this._cachedTimestamp < 5000)) {
if (process.env.DEBUG === 'true') {
console.log('dueCards client: using cached results,', this._cachedCards.length, 'cards');
}
return this._cachedCards; return this._cachedCards;
} }
@ -119,23 +194,56 @@ class DueCardsComponent extends BlazeComponent {
dueAt: { $exists: true, $nin: [null, ''] } dueAt: { $exists: true, $nin: [null, ''] }
}); });
if (process.env.DEBUG === 'true') {
console.log('dueCards client: found', cards.length, 'cards with due dates');
console.log('dueCards client: cards details:', cards.map(c => ({
id: c._id,
title: c.title,
dueAt: c.dueAt,
boardId: c.boardId,
members: c.members,
assignees: c.assignees,
userId: c.userId
})));
}
// Filter cards based on user view preference // Filter cards based on user view preference
const allUsers = this.dueCardsView() === 'all'; const allUsers = this.dueCardsView() === 'all';
const currentUser = ReactiveCache.getCurrentUser(); const currentUser = ReactiveCache.getCurrentUser();
let filteredCards = cards; let filteredCards = cards;
if (process.env.DEBUG === 'true') {
console.log('dueCards client: current user:', currentUser ? currentUser._id : 'none');
console.log('dueCards client: showing all users:', allUsers);
}
if (!allUsers && currentUser) { if (!allUsers && currentUser) {
filteredCards = cards.filter(card => { filteredCards = cards.filter(card => {
return card.members && card.members.includes(currentUser._id) || const isMember = card.members && card.members.includes(currentUser._id);
card.assignees && card.assignees.includes(currentUser._id) || const isAssignee = card.assignees && card.assignees.includes(currentUser._id);
card.userId === currentUser._id; const isAuthor = card.userId === currentUser._id;
const matches = isMember || isAssignee || isAuthor;
if (process.env.DEBUG === 'true' && matches) {
console.log('dueCards client: card matches user:', card.title, { isMember, isAssignee, isAuthor });
}
return matches;
}); });
} }
if (process.env.DEBUG === 'true') {
console.log('dueCards client: filtered to', filteredCards.length, 'cards');
}
// Cache the results for 5 seconds to avoid re-filtering on every render // Cache the results for 5 seconds to avoid re-filtering on every render
this._cachedCards = filteredCards; this._cachedCards = filteredCards;
this._cachedTimestamp = Date.now(); this._cachedTimestamp = Date.now();
// Update reactive variables
this.hasResults.set(filteredCards && filteredCards.length > 0);
this.isLoading.set(false);
return filteredCards; return filteredCards;
} }
} }

View file

@ -1015,6 +1015,8 @@
"dueCardsViewChange-choice-me": "Me", "dueCardsViewChange-choice-me": "Me",
"dueCardsViewChange-choice-all": "All Users", "dueCardsViewChange-choice-all": "All Users",
"dueCardsViewChange-choice-all-description": "Shows all incomplete cards with a *Due* date from boards for which the user has permission.", "dueCardsViewChange-choice-all-description": "Shows all incomplete cards with a *Due* date from boards for which the user has permission.",
"dueCards-noResults-title": "No Due Cards Found",
"dueCards-noResults-description": "You don't have any cards with due dates at the moment.",
"broken-cards": "Broken Cards", "broken-cards": "Broken Cards",
"board-title-not-found": "Board '%s' not found.", "board-title-not-found": "Board '%s' not found.",
"swimlane-title-not-found": "Swimlane '%s' not found.", "swimlane-title-not-found": "Swimlane '%s' not found.",

View file

@ -25,7 +25,7 @@ Meteor.methods({
for (const board of allBoards) { for (const board of allBoards) {
try { try {
const result = this.fixDuplicateListsForBoard(board._id); const result = fixDuplicateListsForBoard(board._id);
totalFixed += result.fixed; totalFixed += result.fixed;
totalBoardsProcessed++; totalBoardsProcessed++;
@ -55,19 +55,21 @@ Meteor.methods({
throw new Meteor.Error('not-authorized'); throw new Meteor.Error('not-authorized');
} }
return this.fixDuplicateListsForBoard(boardId); return fixDuplicateListsForBoard(boardId);
}, }
});
fixDuplicateListsForBoard(boardId) { // Helper functions defined outside of Meteor.methods
function fixDuplicateListsForBoard(boardId) {
if (process.env.DEBUG === 'true') { if (process.env.DEBUG === 'true') {
console.log(`Fixing duplicate lists for board ${boardId}...`); console.log(`Fixing duplicate lists for board ${boardId}...`);
} }
// First, fix duplicate swimlanes // First, fix duplicate swimlanes
const swimlaneResult = this.fixDuplicateSwimlanes(boardId); const swimlaneResult = fixDuplicateSwimlanes(boardId);
// Then, fix duplicate lists // Then, fix duplicate lists
const listResult = this.fixDuplicateLists(boardId); const listResult = fixDuplicateLists(boardId);
return { return {
boardId, boardId,
@ -75,9 +77,10 @@ Meteor.methods({
fixedLists: listResult.fixed, fixedLists: listResult.fixed,
fixed: swimlaneResult.fixed + listResult.fixed fixed: swimlaneResult.fixed + listResult.fixed
}; };
}, }
fixDuplicateSwimlanes(boardId) { // Helper functions defined outside of Meteor.methods
function fixDuplicateSwimlanes(boardId) {
const swimlanes = Swimlanes.find({ boardId }).fetch(); const swimlanes = Swimlanes.find({ boardId }).fetch();
const swimlaneGroups = {}; const swimlaneGroups = {};
let fixed = 0; let fixed = 0;
@ -144,9 +147,9 @@ Meteor.methods({
}); });
return { fixed }; return { fixed };
}, }
fixDuplicateLists(boardId) { function fixDuplicateLists(boardId) {
const lists = Lists.find({ boardId }).fetch(); const lists = Lists.find({ boardId }).fetch();
const listGroups = {}; const listGroups = {};
let fixed = 0; let fixed = 0;
@ -192,8 +195,9 @@ Meteor.methods({
}); });
return { fixed }; return { fixed };
}, }
Meteor.methods({
'fixDuplicateLists.getReport'() { 'fixDuplicateLists.getReport'() {
if (!this.userId) { if (!this.userId) {
throw new Meteor.Error('not-authorized'); throw new Meteor.Error('not-authorized');

View file

@ -136,10 +136,29 @@ Meteor.publish('dueCards', function(allUsers = false) {
// Get user's board memberships for efficient filtering // Get user's board memberships for efficient filtering
const userBoards = ReactiveCache.getBoards({ const userBoards = ReactiveCache.getBoards({
members: userId $or: [
{ permission: 'public' },
{ members: { $elemMatch: { userId, isActive: true } } }
]
}).map(board => board._id); }).map(board => board._id);
if (process.env.DEBUG === 'true') {
console.log('dueCards userBoards:', userBoards);
console.log('dueCards userBoards count:', userBoards.length);
// Also check if there are any cards with due dates in the system at all
const allCardsWithDueDates = Cards.find({
type: 'cardType-card',
archived: false,
dueAt: { $exists: true, $nin: [null, ''] }
}).count();
console.log('dueCards: total cards with due dates in system:', allCardsWithDueDates);
}
if (userBoards.length === 0) { if (userBoards.length === 0) {
if (process.env.DEBUG === 'true') {
console.log('dueCards: No boards found for user, returning ready');
}
return this.ready(); return this.ready();
} }
@ -182,7 +201,23 @@ Meteor.publish('dueCards', function(allUsers = false) {
console.log('dueCards options:', JSON.stringify(options, null, 2)); console.log('dueCards options:', JSON.stringify(options, null, 2));
} }
return Cards.find(selector, options); const result = Cards.find(selector, options);
if (process.env.DEBUG === 'true') {
const count = result.count();
console.log('dueCards publication: returning', count, 'cards');
if (count > 0) {
const sampleCards = result.fetch().slice(0, 3);
console.log('dueCards publication: sample cards:', sampleCards.map(c => ({
id: c._id,
title: c.title,
dueAt: c.dueAt,
boardId: c.boardId
})));
}
}
return result;
}); });
Meteor.publish('globalSearch', function(sessionId, params, text) { Meteor.publish('globalSearch', function(sessionId, params, text) {