mirror of
https://github.com/wekan/wekan.git
synced 2025-12-16 23:40:13 +01:00
Fix Regression - due date taking a while to load all cards v8.06.
Thanks to xet7 ! Fixes #5955
This commit is contained in:
parent
07ce151508
commit
347fa9e5cd
4 changed files with 128 additions and 143 deletions
|
|
@ -246,57 +246,30 @@ class CardDueDate extends CardDate {
|
||||||
const theDate = this.date.get();
|
const theDate = this.date.get();
|
||||||
const now = this.now.get();
|
const now = this.now.get();
|
||||||
|
|
||||||
// Debug logging for due date classes
|
|
||||||
if (process.env.DEBUG === 'true') {
|
|
||||||
console.log(`CardDueDate classes() - Card: "${this.data().title}", Due: ${theDate}, Now: ${now}, End: ${endAt}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
// If there's an end date and it's before the due date, task is completed early
|
// If there's an end date and it's before the due date, task is completed early
|
||||||
if (endAt && isBefore(endAt, theDate)) {
|
if (endAt && isBefore(endAt, theDate)) {
|
||||||
classes += 'completed-early';
|
classes += 'completed-early';
|
||||||
if (process.env.DEBUG === 'true') {
|
|
||||||
console.log(` -> completed-early (end date ${endAt} is before due date ${theDate})`);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
// If there's an end date, don't show due date status since task is completed
|
// If there's an end date, don't show due date status since task is completed
|
||||||
else if (endAt) {
|
else if (endAt) {
|
||||||
classes += 'completed';
|
classes += 'completed';
|
||||||
if (process.env.DEBUG === 'true') {
|
|
||||||
console.log(` -> completed (has end date ${endAt})`);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
// Due date logic based on current time
|
// Due date logic based on current time
|
||||||
else {
|
else {
|
||||||
const daysDiff = diff(theDate, now, 'days');
|
const daysDiff = diff(theDate, now, 'days');
|
||||||
if (process.env.DEBUG === 'true') {
|
|
||||||
console.log(` -> daysDiff: ${daysDiff} (due: ${theDate}, now: ${now})`);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (daysDiff < 0) {
|
if (daysDiff < 0) {
|
||||||
// Due date is in the past - overdue
|
// Due date is in the past - overdue
|
||||||
classes += 'overdue';
|
classes += 'overdue';
|
||||||
if (process.env.DEBUG === 'true') {
|
|
||||||
console.log(` -> overdue (${Math.abs(daysDiff)} days past due)`);
|
|
||||||
}
|
|
||||||
} else if (daysDiff <= 1) {
|
} else if (daysDiff <= 1) {
|
||||||
// Due today or tomorrow - due soon
|
// Due today or tomorrow - due soon
|
||||||
classes += 'due-soon';
|
classes += 'due-soon';
|
||||||
if (process.env.DEBUG === 'true') {
|
|
||||||
console.log(` -> due-soon (due in ${daysDiff} days)`);
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
// Due date is more than 1 day away - not due yet
|
// Due date is more than 1 day away - not due yet
|
||||||
classes += 'not-due';
|
classes += 'not-due';
|
||||||
if (process.env.DEBUG === 'true') {
|
|
||||||
console.log(` -> not-due (due in ${daysDiff} days)`);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (process.env.DEBUG === 'true') {
|
|
||||||
console.log(` -> Final classes: "${classes}"`);
|
|
||||||
}
|
|
||||||
|
|
||||||
return classes;
|
return classes;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,5 @@
|
||||||
import { ReactiveCache } from '/imports/reactiveCache';
|
import { ReactiveCache } from '/imports/reactiveCache';
|
||||||
import { CardSearchPagedComponent } from '../../lib/cardSearch';
|
import { BlazeComponent } from 'meteor/peerlibrary:blaze-components';
|
||||||
import {
|
|
||||||
OPERATOR_HAS,
|
|
||||||
OPERATOR_SORT,
|
|
||||||
OPERATOR_USER,
|
|
||||||
ORDER_ASCENDING,
|
|
||||||
PREDICATE_DUE_AT,
|
|
||||||
} from '../../../config/search-const';
|
|
||||||
import { QueryParams } from '../../../config/query-classes';
|
|
||||||
|
|
||||||
// const subManager = new SubsManager();
|
// const subManager = new SubsManager();
|
||||||
|
|
||||||
|
|
@ -38,6 +30,23 @@ Template.dueCards.helpers({
|
||||||
}
|
}
|
||||||
return [];
|
return [];
|
||||||
},
|
},
|
||||||
|
hasResults() {
|
||||||
|
const component = BlazeComponent.getComponentForElement(this);
|
||||||
|
if (component && component.dueCardsList) {
|
||||||
|
const cards = component.dueCardsList();
|
||||||
|
return cards && cards.length > 0;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
searching() {
|
||||||
|
return false; // No longer using search, so always false
|
||||||
|
},
|
||||||
|
hasQueryErrors() {
|
||||||
|
return false; // No longer using search, so always false
|
||||||
|
},
|
||||||
|
errorMessages() {
|
||||||
|
return []; // No longer using search, so always empty
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
BlazeComponent.extendComponent({
|
BlazeComponent.extendComponent({
|
||||||
|
|
@ -62,71 +71,29 @@ BlazeComponent.extendComponent({
|
||||||
},
|
},
|
||||||
}).register('dueCardsViewChangePopup');
|
}).register('dueCardsViewChangePopup');
|
||||||
|
|
||||||
class DueCardsComponent extends CardSearchPagedComponent {
|
class DueCardsComponent extends BlazeComponent {
|
||||||
onCreated() {
|
onCreated() {
|
||||||
super.onCreated();
|
super.onCreated();
|
||||||
|
|
||||||
// Add a small delay to ensure ReactiveCache is ready
|
this._cachedCards = null;
|
||||||
this.searchRetryCount = 0;
|
this._cachedTimestamp = null;
|
||||||
this.maxRetries = 3;
|
this.subscriptionHandle = null;
|
||||||
|
|
||||||
// Use a timeout to ensure the search runs after the component is fully initialized
|
// Subscribe to the optimized due cards publication
|
||||||
Meteor.setTimeout(() => {
|
this.autorun(() => {
|
||||||
this.performSearch();
|
const allUsers = this.dueCardsView() === 'all';
|
||||||
}, 100);
|
if (this.subscriptionHandle) {
|
||||||
}
|
this.subscriptionHandle.stop();
|
||||||
|
|
||||||
performSearch() {
|
|
||||||
if (process.env.DEBUG === 'true') {
|
|
||||||
console.log('Performing due cards search, attempt:', this.searchRetryCount + 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if user is authenticated
|
|
||||||
const currentUser = ReactiveCache.getCurrentUser();
|
|
||||||
if (!currentUser) {
|
|
||||||
if (process.env.DEBUG === 'true') {
|
|
||||||
console.log('User not authenticated, waiting...');
|
|
||||||
}
|
}
|
||||||
Meteor.setTimeout(() => {
|
this.subscriptionHandle = Meteor.subscribe('dueCards', allUsers);
|
||||||
this.performSearch();
|
|
||||||
}, 1000);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (process.env.DEBUG === 'true') {
|
|
||||||
console.log('User authenticated:', currentUser.username);
|
|
||||||
}
|
|
||||||
|
|
||||||
const queryParams = new QueryParams();
|
|
||||||
queryParams.addPredicate(OPERATOR_HAS, {
|
|
||||||
field: PREDICATE_DUE_AT,
|
|
||||||
exists: true,
|
|
||||||
});
|
|
||||||
// queryParams[OPERATOR_LIMIT] = 5;
|
|
||||||
queryParams.addPredicate(OPERATOR_SORT, {
|
|
||||||
name: PREDICATE_DUE_AT,
|
|
||||||
order: ORDER_ASCENDING,
|
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// Note: User filtering is handled server-side based on board membership
|
onDestroyed() {
|
||||||
// The OPERATOR_USER filter is too restrictive as it only shows cards where
|
super.onDestroyed();
|
||||||
// the user is assigned or a member of the card, not the board
|
if (this.subscriptionHandle) {
|
||||||
// if (Utils && Utils.dueCardsView && Utils.dueCardsView() !== 'all') {
|
this.subscriptionHandle.stop();
|
||||||
// const currentUser = ReactiveCache.getCurrentUser();
|
|
||||||
// if (currentUser && currentUser.username) {
|
|
||||||
// queryParams.addPredicate(OPERATOR_USER, currentUser.username);
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
// Debug: Log the query parameters
|
|
||||||
if (process.env.DEBUG === 'true') {
|
|
||||||
console.log('Due cards query params:', queryParams.params);
|
|
||||||
console.log('Due cards query text:', queryParams.text);
|
|
||||||
console.log('Due cards has predicates:', queryParams.getPredicates('has'));
|
|
||||||
console.log('Due cards sort predicates:', queryParams.getPredicates('sort'));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
this.runGlobalSearch(queryParams);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
dueCardsView() {
|
dueCardsView() {
|
||||||
|
|
@ -140,36 +107,36 @@ class DueCardsComponent extends CardSearchPagedComponent {
|
||||||
}
|
}
|
||||||
|
|
||||||
dueCardsList() {
|
dueCardsList() {
|
||||||
const results = this.getResults();
|
// Use cached results if available to avoid expensive re-sorting
|
||||||
console.log('results:', results);
|
if (this._cachedCards && this._cachedTimestamp && (Date.now() - this._cachedTimestamp < 5000)) {
|
||||||
const cards = [];
|
return this._cachedCards;
|
||||||
if (results) {
|
}
|
||||||
results.forEach(card => {
|
|
||||||
cards.push(card);
|
// Get cards directly from the subscription (already sorted by the publication)
|
||||||
|
const cards = ReactiveCache.getCards({
|
||||||
|
type: 'cardType-card',
|
||||||
|
archived: false,
|
||||||
|
dueAt: { $exists: true, $nin: [null, ''] }
|
||||||
|
});
|
||||||
|
|
||||||
|
// Filter cards based on user view preference
|
||||||
|
const allUsers = this.dueCardsView() === 'all';
|
||||||
|
const currentUser = ReactiveCache.getCurrentUser();
|
||||||
|
let filteredCards = cards;
|
||||||
|
|
||||||
|
if (!allUsers && currentUser) {
|
||||||
|
filteredCards = cards.filter(card => {
|
||||||
|
return card.members && card.members.includes(currentUser._id) ||
|
||||||
|
card.assignees && card.assignees.includes(currentUser._id) ||
|
||||||
|
card.userId === currentUser._id;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sort by due date: oldest first (ascending order)
|
// Cache the results for 5 seconds to avoid re-filtering on every render
|
||||||
cards.sort((a, b) => {
|
this._cachedCards = filteredCards;
|
||||||
// Handle null/undefined due dates by putting them at the end
|
this._cachedTimestamp = Date.now();
|
||||||
const aDueAt = a.dueAt ? new Date(a.dueAt) : new Date('2100-12-31');
|
|
||||||
const bDueAt = b.dueAt ? new Date(b.dueAt) : new Date('2100-12-31');
|
|
||||||
|
|
||||||
// Debug logging
|
return filteredCards;
|
||||||
if (process.env.DEBUG === 'true') {
|
|
||||||
console.log(`Comparing cards: "${a.title}" (${a.dueAt}) vs "${b.title}" (${b.dueAt})`);
|
|
||||||
console.log(`Parsed dates: ${aDueAt.toISOString()} vs ${bDueAt.toISOString()}`);
|
|
||||||
console.log(`Time difference: ${aDueAt.getTime() - bDueAt.getTime()}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Compare dates: if a is earlier than b, return negative (a comes first)
|
|
||||||
// if a is later than b, return positive (b comes first)
|
|
||||||
return aDueAt.getTime() - bDueAt.getTime();
|
|
||||||
});
|
|
||||||
|
|
||||||
// eslint-disable-next-line no-console
|
|
||||||
console.log('cards sorted by due date (oldest first):', cards);
|
|
||||||
return cards;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -179,11 +179,16 @@ export class CardSearchPagedComponent extends BlazeComponent {
|
||||||
console.log('getResults - no sessionData or no cards array, trying direct card search');
|
console.log('getResults - no sessionData or no cards array, trying direct card search');
|
||||||
}
|
}
|
||||||
// Fallback: try to get cards directly from the client-side collection
|
// Fallback: try to get cards directly from the client-side collection
|
||||||
|
// Use a more efficient query with limit and sort
|
||||||
const selector = {
|
const selector = {
|
||||||
type: 'cardType-card',
|
type: 'cardType-card',
|
||||||
dueAt: { $exists: true, $nin: [null, ''] }
|
dueAt: { $exists: true, $nin: [null, ''] }
|
||||||
};
|
};
|
||||||
const allCards = Cards.find(selector).fetch();
|
const options = {
|
||||||
|
sort: { dueAt: 1 }, // Sort by due date ascending (oldest first)
|
||||||
|
limit: 100 // Limit to 100 cards for performance
|
||||||
|
};
|
||||||
|
const allCards = Cards.find(selector, options).fetch();
|
||||||
if (process.env.DEBUG === 'true') {
|
if (process.env.DEBUG === 'true') {
|
||||||
console.log('getResults - direct card search found:', allCards ? allCards.length : 0, 'cards');
|
console.log('getResults - direct card search found:', allCards ? allCards.length : 0, 'cards');
|
||||||
}
|
}
|
||||||
|
|
@ -191,9 +196,6 @@ export class CardSearchPagedComponent extends BlazeComponent {
|
||||||
if (allCards && allCards.length > 0) {
|
if (allCards && allCards.length > 0) {
|
||||||
allCards.forEach(card => {
|
allCards.forEach(card => {
|
||||||
if (card && card._id) {
|
if (card && card._id) {
|
||||||
if (process.env.DEBUG === 'true') {
|
|
||||||
console.log('getResults - direct card:', card._id, card.title);
|
|
||||||
}
|
|
||||||
cards.push(card);
|
cards.push(card);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -121,26 +121,69 @@ Meteor.publish('myCards', function(sessionId) {
|
||||||
return ret;
|
return ret;
|
||||||
});
|
});
|
||||||
|
|
||||||
// Meteor.publish('dueCards', function(sessionId, allUsers = false) {
|
// Optimized due cards publication for better performance
|
||||||
// check(sessionId, String);
|
Meteor.publish('dueCards', function(allUsers = false) {
|
||||||
// check(allUsers, Boolean);
|
check(allUsers, Boolean);
|
||||||
//
|
|
||||||
// // eslint-disable-next-line no-console
|
const userId = this.userId;
|
||||||
// // console.log('all users:', allUsers);
|
if (!userId) {
|
||||||
//
|
return this.ready();
|
||||||
// const queryParams = {
|
}
|
||||||
// has: [{ field: 'dueAt', exists: true }],
|
|
||||||
// limit: 25,
|
if (process.env.DEBUG === 'true') {
|
||||||
// skip: 0,
|
console.log('dueCards publication called for user:', userId, 'allUsers:', allUsers);
|
||||||
// sort: { name: 'dueAt', order: 'des' },
|
}
|
||||||
// };
|
|
||||||
//
|
// Get user's board memberships for efficient filtering
|
||||||
// if (!allUsers) {
|
const userBoards = ReactiveCache.getBoards({
|
||||||
// queryParams.users = [ReactiveCache.getCurrentUser().username];
|
members: userId
|
||||||
// }
|
}).map(board => board._id);
|
||||||
//
|
|
||||||
// return buildQuery(sessionId, queryParams);
|
if (userBoards.length === 0) {
|
||||||
// });
|
return this.ready();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Build optimized selector
|
||||||
|
const selector = {
|
||||||
|
type: 'cardType-card',
|
||||||
|
archived: false,
|
||||||
|
dueAt: { $exists: true, $nin: [null, ''] },
|
||||||
|
boardId: { $in: userBoards }
|
||||||
|
};
|
||||||
|
|
||||||
|
// Add user filtering if not showing all users
|
||||||
|
if (!allUsers) {
|
||||||
|
selector.$or = [
|
||||||
|
{ members: userId },
|
||||||
|
{ assignees: userId },
|
||||||
|
{ userId: userId }
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
const options = {
|
||||||
|
sort: { dueAt: 1 }, // Sort by due date ascending (oldest first)
|
||||||
|
limit: 100, // Limit results for performance
|
||||||
|
fields: {
|
||||||
|
title: 1,
|
||||||
|
dueAt: 1,
|
||||||
|
boardId: 1,
|
||||||
|
listId: 1,
|
||||||
|
swimlaneId: 1,
|
||||||
|
members: 1,
|
||||||
|
assignees: 1,
|
||||||
|
userId: 1,
|
||||||
|
archived: 1,
|
||||||
|
type: 1
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if (process.env.DEBUG === 'true') {
|
||||||
|
console.log('dueCards selector:', JSON.stringify(selector, null, 2));
|
||||||
|
console.log('dueCards options:', JSON.stringify(options, null, 2));
|
||||||
|
}
|
||||||
|
|
||||||
|
return Cards.find(selector, options);
|
||||||
|
});
|
||||||
|
|
||||||
Meteor.publish('globalSearch', function(sessionId, params, text) {
|
Meteor.publish('globalSearch', function(sessionId, params, text) {
|
||||||
check(sessionId, String);
|
check(sessionId, String);
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue