mirror of
https://github.com/wekan/wekan.git
synced 2025-12-16 15:30: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 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 (endAt && isBefore(endAt, theDate)) {
|
||||
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
|
||||
else if (endAt) {
|
||||
classes += 'completed';
|
||||
if (process.env.DEBUG === 'true') {
|
||||
console.log(` -> completed (has end date ${endAt})`);
|
||||
}
|
||||
}
|
||||
// Due date logic based on current time
|
||||
else {
|
||||
const daysDiff = diff(theDate, now, 'days');
|
||||
if (process.env.DEBUG === 'true') {
|
||||
console.log(` -> daysDiff: ${daysDiff} (due: ${theDate}, now: ${now})`);
|
||||
}
|
||||
|
||||
if (daysDiff < 0) {
|
||||
// Due date is in the past - overdue
|
||||
classes += 'overdue';
|
||||
if (process.env.DEBUG === 'true') {
|
||||
console.log(` -> overdue (${Math.abs(daysDiff)} days past due)`);
|
||||
}
|
||||
} else if (daysDiff <= 1) {
|
||||
// Due today or tomorrow - due soon
|
||||
classes += 'due-soon';
|
||||
if (process.env.DEBUG === 'true') {
|
||||
console.log(` -> due-soon (due in ${daysDiff} days)`);
|
||||
}
|
||||
} else {
|
||||
// Due date is more than 1 day away - not due yet
|
||||
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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,13 +1,5 @@
|
|||
import { ReactiveCache } from '/imports/reactiveCache';
|
||||
import { CardSearchPagedComponent } from '../../lib/cardSearch';
|
||||
import {
|
||||
OPERATOR_HAS,
|
||||
OPERATOR_SORT,
|
||||
OPERATOR_USER,
|
||||
ORDER_ASCENDING,
|
||||
PREDICATE_DUE_AT,
|
||||
} from '../../../config/search-const';
|
||||
import { QueryParams } from '../../../config/query-classes';
|
||||
import { BlazeComponent } from 'meteor/peerlibrary:blaze-components';
|
||||
|
||||
// const subManager = new SubsManager();
|
||||
|
||||
|
|
@ -38,6 +30,23 @@ Template.dueCards.helpers({
|
|||
}
|
||||
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({
|
||||
|
|
@ -62,71 +71,29 @@ BlazeComponent.extendComponent({
|
|||
},
|
||||
}).register('dueCardsViewChangePopup');
|
||||
|
||||
class DueCardsComponent extends CardSearchPagedComponent {
|
||||
class DueCardsComponent extends BlazeComponent {
|
||||
onCreated() {
|
||||
super.onCreated();
|
||||
|
||||
// Add a small delay to ensure ReactiveCache is ready
|
||||
this.searchRetryCount = 0;
|
||||
this.maxRetries = 3;
|
||||
this._cachedCards = null;
|
||||
this._cachedTimestamp = null;
|
||||
this.subscriptionHandle = null;
|
||||
|
||||
// Use a timeout to ensure the search runs after the component is fully initialized
|
||||
Meteor.setTimeout(() => {
|
||||
this.performSearch();
|
||||
}, 100);
|
||||
// Subscribe to the optimized due cards publication
|
||||
this.autorun(() => {
|
||||
const allUsers = this.dueCardsView() === 'all';
|
||||
if (this.subscriptionHandle) {
|
||||
this.subscriptionHandle.stop();
|
||||
}
|
||||
this.subscriptionHandle = Meteor.subscribe('dueCards', allUsers);
|
||||
});
|
||||
}
|
||||
|
||||
performSearch() {
|
||||
if (process.env.DEBUG === 'true') {
|
||||
console.log('Performing due cards search, attempt:', this.searchRetryCount + 1);
|
||||
onDestroyed() {
|
||||
super.onDestroyed();
|
||||
if (this.subscriptionHandle) {
|
||||
this.subscriptionHandle.stop();
|
||||
}
|
||||
|
||||
// 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.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
|
||||
// The OPERATOR_USER filter is too restrictive as it only shows cards where
|
||||
// the user is assigned or a member of the card, not the board
|
||||
// if (Utils && Utils.dueCardsView && Utils.dueCardsView() !== 'all') {
|
||||
// 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() {
|
||||
|
|
@ -140,36 +107,36 @@ class DueCardsComponent extends CardSearchPagedComponent {
|
|||
}
|
||||
|
||||
dueCardsList() {
|
||||
const results = this.getResults();
|
||||
console.log('results:', results);
|
||||
const cards = [];
|
||||
if (results) {
|
||||
results.forEach(card => {
|
||||
cards.push(card);
|
||||
// Use cached results if available to avoid expensive re-sorting
|
||||
if (this._cachedCards && this._cachedTimestamp && (Date.now() - this._cachedTimestamp < 5000)) {
|
||||
return this._cachedCards;
|
||||
}
|
||||
|
||||
// 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)
|
||||
cards.sort((a, b) => {
|
||||
// Handle null/undefined due dates by putting them at the end
|
||||
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');
|
||||
// Cache the results for 5 seconds to avoid re-filtering on every render
|
||||
this._cachedCards = filteredCards;
|
||||
this._cachedTimestamp = Date.now();
|
||||
|
||||
// Debug logging
|
||||
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;
|
||||
return filteredCards;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -179,11 +179,16 @@ export class CardSearchPagedComponent extends BlazeComponent {
|
|||
console.log('getResults - no sessionData or no cards array, trying direct card search');
|
||||
}
|
||||
// Fallback: try to get cards directly from the client-side collection
|
||||
// Use a more efficient query with limit and sort
|
||||
const selector = {
|
||||
type: 'cardType-card',
|
||||
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') {
|
||||
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) {
|
||||
allCards.forEach(card => {
|
||||
if (card && card._id) {
|
||||
if (process.env.DEBUG === 'true') {
|
||||
console.log('getResults - direct card:', card._id, card.title);
|
||||
}
|
||||
cards.push(card);
|
||||
}
|
||||
});
|
||||
|
|
|
|||
|
|
@ -121,26 +121,69 @@ Meteor.publish('myCards', function(sessionId) {
|
|||
return ret;
|
||||
});
|
||||
|
||||
// Meteor.publish('dueCards', function(sessionId, allUsers = false) {
|
||||
// check(sessionId, String);
|
||||
// check(allUsers, Boolean);
|
||||
//
|
||||
// // eslint-disable-next-line no-console
|
||||
// // console.log('all users:', allUsers);
|
||||
//
|
||||
// const queryParams = {
|
||||
// has: [{ field: 'dueAt', exists: true }],
|
||||
// limit: 25,
|
||||
// skip: 0,
|
||||
// sort: { name: 'dueAt', order: 'des' },
|
||||
// };
|
||||
//
|
||||
// if (!allUsers) {
|
||||
// queryParams.users = [ReactiveCache.getCurrentUser().username];
|
||||
// }
|
||||
//
|
||||
// return buildQuery(sessionId, queryParams);
|
||||
// });
|
||||
// Optimized due cards publication for better performance
|
||||
Meteor.publish('dueCards', function(allUsers = false) {
|
||||
check(allUsers, Boolean);
|
||||
|
||||
const userId = this.userId;
|
||||
if (!userId) {
|
||||
return this.ready();
|
||||
}
|
||||
|
||||
if (process.env.DEBUG === 'true') {
|
||||
console.log('dueCards publication called for user:', userId, 'allUsers:', allUsers);
|
||||
}
|
||||
|
||||
// Get user's board memberships for efficient filtering
|
||||
const userBoards = ReactiveCache.getBoards({
|
||||
members: userId
|
||||
}).map(board => board._id);
|
||||
|
||||
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) {
|
||||
check(sessionId, String);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue