mirror of
https://github.com/wekan/wekan.git
synced 2025-12-16 23:40:13 +01:00
Update Global Search, Due Cards, and My Cards to use the same
code for searching and display
This commit is contained in:
parent
8181074238
commit
a1bda1169e
7 changed files with 266 additions and 637 deletions
|
|
@ -22,13 +22,17 @@ template(name="dueCardsModalTitle")
|
|||
|
||||
template(name="dueCards")
|
||||
if currentUser
|
||||
if isPageReady.get
|
||||
.wrapper
|
||||
.due-cards-dueat-list-wrapper
|
||||
each card in dueCardsList
|
||||
+resultCard(card)
|
||||
else
|
||||
if searching.get
|
||||
+spinner
|
||||
else if hasResults.get
|
||||
.global-search-results-list-wrapper
|
||||
if hasQueryErrors.get
|
||||
div
|
||||
each msg in errorMessages
|
||||
span.global-search-error-messages
|
||||
= msg
|
||||
else
|
||||
+resultsPaged(this)
|
||||
|
||||
template(name="dueCardsViewChangePopup")
|
||||
if currentUser
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
import { CardSearchPagedComponent } from '../../lib/cardSearch';
|
||||
|
||||
const subManager = new SubsManager();
|
||||
|
||||
BlazeComponent.extendComponent({
|
||||
|
|
@ -40,102 +42,43 @@ BlazeComponent.extendComponent({
|
|||
},
|
||||
}).register('dueCardsViewChangePopup');
|
||||
|
||||
BlazeComponent.extendComponent({
|
||||
class DueCardsComponent extends CardSearchPagedComponent {
|
||||
onCreated() {
|
||||
this.isPageReady = new ReactiveVar(false);
|
||||
super.onCreated();
|
||||
|
||||
this.autorun(() => {
|
||||
const handle = subManager.subscribe(
|
||||
'dueCards',
|
||||
Utils.dueCardsView() === 'all',
|
||||
);
|
||||
Tracker.nonreactive(() => {
|
||||
Tracker.autorun(() => {
|
||||
this.isPageReady.set(handle.ready());
|
||||
});
|
||||
});
|
||||
});
|
||||
Meteor.subscribe('setting');
|
||||
},
|
||||
const queryParams = {
|
||||
has: [{ field: 'dueAt', exists: true }],
|
||||
limit: 5,
|
||||
skip: 0,
|
||||
sort: { name: 'dueAt', order: 'des' },
|
||||
};
|
||||
|
||||
if (Utils.dueCardsView() !== 'all') {
|
||||
queryParams.users = [Meteor.user().username];
|
||||
}
|
||||
|
||||
this.autorunGlobalSearch(queryParams);
|
||||
}
|
||||
|
||||
dueCardsView() {
|
||||
// eslint-disable-next-line no-console
|
||||
//console.log('sort:', Utils.dueCardsView());
|
||||
return Utils.dueCardsView();
|
||||
},
|
||||
}
|
||||
|
||||
sortByBoard() {
|
||||
return this.dueCardsView() === 'board';
|
||||
},
|
||||
}
|
||||
|
||||
dueCardsList() {
|
||||
const allUsers = Utils.dueCardsView() === 'all';
|
||||
|
||||
const user = Meteor.user();
|
||||
|
||||
const archivedBoards = [];
|
||||
Boards.find({ archived: true }).forEach(board => {
|
||||
archivedBoards.push(board._id);
|
||||
});
|
||||
|
||||
const permiitedBoards = [];
|
||||
let selector = {
|
||||
archived: false,
|
||||
};
|
||||
// for every user including admin allow her to see cards only from public boards
|
||||
// or those where she is a member
|
||||
//if (!user.isAdmin) {
|
||||
selector.$or = [
|
||||
{ permission: 'public' },
|
||||
{ members: { $elemMatch: { userId: user._id, isActive: true } } },
|
||||
];
|
||||
//}
|
||||
Boards.find(selector).forEach(board => {
|
||||
permiitedBoards.push(board._id);
|
||||
});
|
||||
|
||||
const archivedSwimlanes = [];
|
||||
Swimlanes.find({ archived: true }).forEach(swimlane => {
|
||||
archivedSwimlanes.push(swimlane._id);
|
||||
});
|
||||
|
||||
const archivedLists = [];
|
||||
Lists.find({ archived: true }).forEach(list => {
|
||||
archivedLists.push(list._id);
|
||||
});
|
||||
|
||||
selector = {
|
||||
archived: false,
|
||||
boardId: {
|
||||
$nin: archivedBoards,
|
||||
$in: permiitedBoards,
|
||||
},
|
||||
swimlaneId: { $nin: archivedSwimlanes },
|
||||
listId: { $nin: archivedLists },
|
||||
dueAt: { $ne: null },
|
||||
endAt: null,
|
||||
};
|
||||
|
||||
if (!allUsers) {
|
||||
selector.$or = [{ members: user._id }, { assignees: user._id }];
|
||||
}
|
||||
|
||||
const results = this.getResults();
|
||||
console.log('results:', results);
|
||||
const cards = [];
|
||||
|
||||
// eslint-disable-next-line no-console
|
||||
// console.log('cards selector:', selector);
|
||||
Cards.find(selector).forEach(card => {
|
||||
cards.push(card);
|
||||
// eslint-disable-next-line no-console
|
||||
// console.log(
|
||||
// 'board:',
|
||||
// card.board(),
|
||||
// 'swimlane:',
|
||||
// card.swimlane(),
|
||||
// 'list:',
|
||||
// card.list(),
|
||||
// );
|
||||
});
|
||||
if (results) {
|
||||
results.forEach(card => {
|
||||
cards.push(card);
|
||||
});
|
||||
}
|
||||
|
||||
cards.sort((a, b) => {
|
||||
const x = a.dueAt === null ? Date('2100-12-31') : a.dueAt;
|
||||
|
|
@ -148,7 +91,9 @@ BlazeComponent.extendComponent({
|
|||
});
|
||||
|
||||
// eslint-disable-next-line no-console
|
||||
// console.log('cards:', cards);
|
||||
console.log('cards:', cards);
|
||||
return cards;
|
||||
},
|
||||
}).register('dueCards');
|
||||
}
|
||||
}
|
||||
|
||||
DueCardsComponent.register('dueCards');
|
||||
|
|
|
|||
|
|
@ -10,6 +10,23 @@ template(name="globalSearchModalTitle")
|
|||
i.fa.fa-keyboard-o
|
||||
| {{_ 'globalSearch-title'}}
|
||||
|
||||
template(name="resultsPaged")
|
||||
h1
|
||||
= resultsHeading.get
|
||||
a.fa.fa-link(title="{{_ 'link-to-search' }}" href="{{ getSearchHref }}")
|
||||
each card in results.get
|
||||
+resultCard(card)
|
||||
table.global-search-footer
|
||||
tr
|
||||
td.global-search-previous-page
|
||||
if hasPreviousPage.get
|
||||
button.js-previous-page
|
||||
| {{_ 'previous-page' }}
|
||||
td.global-search-next-page(align="right")
|
||||
if hasNextPage.get
|
||||
button.js-next-page
|
||||
| {{_ 'next-page' }}
|
||||
|
||||
template(name="globalSearch")
|
||||
if currentUser
|
||||
.wrapper
|
||||
|
|
@ -32,21 +49,7 @@ template(name="globalSearch")
|
|||
span.global-search-error-messages
|
||||
= msg
|
||||
else
|
||||
h1
|
||||
= resultsHeading.get
|
||||
a.fa.fa-link(title="{{_ 'link-to-search' }}" href="{{ getSearchHref }}")
|
||||
each card in results.get
|
||||
+resultCard(card)
|
||||
table.global-search-footer
|
||||
tr
|
||||
td.global-search-previous-page
|
||||
if hasPreviousPage.get
|
||||
button.js-previous-page
|
||||
| {{_ 'previous-page' }}
|
||||
td.global-search-next-page(align="right")
|
||||
if hasNextPage.get
|
||||
button.js-next-page
|
||||
| {{_ 'next-page' }}
|
||||
+resultsPaged(this)
|
||||
else
|
||||
.global-search-page
|
||||
.global-search-help
|
||||
|
|
|
|||
|
|
@ -1,4 +1,6 @@
|
|||
const subManager = new SubsManager();
|
||||
import { CardSearchPagedComponent } from '../../lib/cardSearch';
|
||||
|
||||
// const subManager = new SubsManager();
|
||||
|
||||
BlazeComponent.extendComponent({
|
||||
events() {
|
||||
|
|
@ -34,27 +36,15 @@ BlazeComponent.extendComponent({
|
|||
},
|
||||
}).register('globalSearchViewChangePopup');
|
||||
|
||||
BlazeComponent.extendComponent({
|
||||
class GlobalSearchComponent extends CardSearchPagedComponent {
|
||||
onCreated() {
|
||||
this.searching = new ReactiveVar(false);
|
||||
this.hasResults = new ReactiveVar(false);
|
||||
this.hasQueryErrors = new ReactiveVar(false);
|
||||
this.query = new ReactiveVar('');
|
||||
this.resultsHeading = new ReactiveVar('');
|
||||
this.searchLink = new ReactiveVar(null);
|
||||
super.onCreated();
|
||||
this.myLists = new ReactiveVar([]);
|
||||
this.myLabelNames = new ReactiveVar([]);
|
||||
this.myBoardNames = new ReactiveVar([]);
|
||||
this.results = new ReactiveVar([]);
|
||||
this.hasNextPage = new ReactiveVar(false);
|
||||
this.hasPreviousPage = new ReactiveVar(false);
|
||||
this.queryParams = null;
|
||||
this.parsingErrors = [];
|
||||
this.resultsCount = 0;
|
||||
this.totalHits = 0;
|
||||
this.queryErrors = null;
|
||||
this.colorMap = null;
|
||||
this.resultsPerPage = 25;
|
||||
this.queryParams = null;
|
||||
|
||||
Meteor.call('myLists', (err, data) => {
|
||||
if (!err) {
|
||||
|
|
@ -73,7 +63,7 @@ BlazeComponent.extendComponent({
|
|||
this.myBoardNames.set(data);
|
||||
}
|
||||
});
|
||||
},
|
||||
}
|
||||
|
||||
onRendered() {
|
||||
Meteor.subscribe('setting');
|
||||
|
|
@ -87,67 +77,19 @@ BlazeComponent.extendComponent({
|
|||
if (Session.get('globalQuery')) {
|
||||
this.searchAllBoards(Session.get('globalQuery'));
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
resetSearch() {
|
||||
this.searching.set(false);
|
||||
this.results.set([]);
|
||||
this.hasResults.set(false);
|
||||
this.hasQueryErrors.set(false);
|
||||
this.resultsHeading.set('');
|
||||
super.resetSearch();
|
||||
this.parsingErrors = [];
|
||||
this.resultsCount = 0;
|
||||
this.totalHits = 0;
|
||||
this.queryErrors = null;
|
||||
},
|
||||
|
||||
getSessionData() {
|
||||
return SessionData.findOne({
|
||||
userId: Meteor.userId(),
|
||||
sessionId: SessionData.getSessionId(),
|
||||
});
|
||||
},
|
||||
|
||||
getResults() {
|
||||
// eslint-disable-next-line no-console
|
||||
// console.log('getting results');
|
||||
if (this.queryParams) {
|
||||
const sessionData = this.getSessionData();
|
||||
// eslint-disable-next-line no-console
|
||||
// console.log('selector:', sessionData.getSelector());
|
||||
// console.log('session data:', sessionData);
|
||||
const projection = sessionData.getProjection();
|
||||
projection.skip = 0;
|
||||
const cards = Cards.find({ _id: { $in: sessionData.cards } }, projection);
|
||||
this.queryErrors = sessionData.errors;
|
||||
if (this.queryErrors.length) {
|
||||
this.hasQueryErrors.set(true);
|
||||
return null;
|
||||
}
|
||||
|
||||
if (cards) {
|
||||
this.totalHits = sessionData.totalHits;
|
||||
this.resultsCount = cards.count();
|
||||
this.resultsStart = sessionData.lastHit - this.resultsCount + 1;
|
||||
this.resultsEnd = sessionData.lastHit;
|
||||
this.resultsHeading.set(this.getResultsHeading());
|
||||
this.results.set(cards);
|
||||
this.hasNextPage.set(sessionData.lastHit < sessionData.totalHits);
|
||||
this.hasPreviousPage.set(
|
||||
sessionData.lastHit - sessionData.resultsCount > 0,
|
||||
);
|
||||
}
|
||||
}
|
||||
this.resultsCount = 0;
|
||||
return null;
|
||||
},
|
||||
}
|
||||
|
||||
errorMessages() {
|
||||
if (this.parsingErrors.length) {
|
||||
return this.parsingErrorMessages();
|
||||
}
|
||||
return this.queryErrorMessages();
|
||||
},
|
||||
}
|
||||
|
||||
parsingErrorMessages() {
|
||||
const messages = [];
|
||||
|
|
@ -159,21 +101,7 @@ BlazeComponent.extendComponent({
|
|||
}
|
||||
|
||||
return messages;
|
||||
},
|
||||
|
||||
queryErrorMessages() {
|
||||
messages = [];
|
||||
|
||||
this.queryErrors.forEach(err => {
|
||||
let value = err.color ? TAPi18n.__(`color-${err.value}`) : err.value;
|
||||
if (!value) {
|
||||
value = err.value;
|
||||
}
|
||||
messages.push(TAPi18n.__(err.tag, value));
|
||||
});
|
||||
|
||||
return messages;
|
||||
},
|
||||
}
|
||||
|
||||
searchAllBoards(query) {
|
||||
query = query.trim();
|
||||
|
|
@ -300,7 +228,7 @@ BlazeComponent.extendComponent({
|
|||
|
||||
let text = '';
|
||||
while (query) {
|
||||
m = query.match(reOperator1);
|
||||
let m = query.match(reOperator1);
|
||||
if (!m) {
|
||||
m = query.match(reOperator2);
|
||||
if (m) {
|
||||
|
|
@ -326,7 +254,7 @@ BlazeComponent.extendComponent({
|
|||
// console.log('found color:', value);
|
||||
}
|
||||
} else if (['dueAt', 'createdAt', 'modifiedAt'].includes(operator)) {
|
||||
let days = parseInt(value, 10);
|
||||
const days = parseInt(value, 10);
|
||||
let duration = null;
|
||||
if (isNaN(days)) {
|
||||
// duration was specified as text
|
||||
|
|
@ -335,7 +263,8 @@ BlazeComponent.extendComponent({
|
|||
let date = null;
|
||||
switch (duration) {
|
||||
case 'week':
|
||||
let week = moment().week();
|
||||
// eslint-disable-next-line no-case-declarations
|
||||
const week = moment().week();
|
||||
if (week === 52) {
|
||||
date = moment(1, 'W');
|
||||
date.set('year', date.year() + 1);
|
||||
|
|
@ -344,7 +273,8 @@ BlazeComponent.extendComponent({
|
|||
}
|
||||
break;
|
||||
case 'month':
|
||||
let month = moment().month();
|
||||
// eslint-disable-next-line no-case-declarations
|
||||
const month = moment().month();
|
||||
// .month() is zero indexed
|
||||
if (month === 11) {
|
||||
date = moment(1, 'M');
|
||||
|
|
@ -354,7 +284,8 @@ BlazeComponent.extendComponent({
|
|||
}
|
||||
break;
|
||||
case 'quarter':
|
||||
let quarter = moment().quarter();
|
||||
// eslint-disable-next-line no-case-declarations
|
||||
const quarter = moment().quarter();
|
||||
if (quarter === 4) {
|
||||
date = moment(1, 'Q');
|
||||
date.set('year', date.year() + 1);
|
||||
|
|
@ -384,22 +315,20 @@ BlazeComponent.extendComponent({
|
|||
});
|
||||
value = null;
|
||||
}
|
||||
} else if (operator === 'dueAt') {
|
||||
value = {
|
||||
operator: '$lt',
|
||||
value: moment(moment().format('YYYY-MM-DD'))
|
||||
.add(days + 1, duration ? duration : 'days')
|
||||
.format(),
|
||||
};
|
||||
} else {
|
||||
if (operator === 'dueAt') {
|
||||
value = {
|
||||
operator: '$lt',
|
||||
value: moment(moment().format('YYYY-MM-DD'))
|
||||
.add(days + 1, duration ? duration : 'days')
|
||||
.format(),
|
||||
};
|
||||
} else {
|
||||
value = {
|
||||
operator: '$gte',
|
||||
value: moment(moment().format('YYYY-MM-DD'))
|
||||
.subtract(days, duration ? duration : 'days')
|
||||
.format(),
|
||||
};
|
||||
}
|
||||
value = {
|
||||
operator: '$gte',
|
||||
value: moment(moment().format('YYYY-MM-DD'))
|
||||
.subtract(days, duration ? duration : 'days')
|
||||
.format(),
|
||||
};
|
||||
}
|
||||
} else if (operator === 'sort') {
|
||||
let negated = false;
|
||||
|
|
@ -502,81 +431,11 @@ BlazeComponent.extendComponent({
|
|||
return;
|
||||
}
|
||||
|
||||
this.autorun(() => {
|
||||
const handle = Meteor.subscribe(
|
||||
'globalSearch',
|
||||
SessionData.getSessionId(),
|
||||
params,
|
||||
);
|
||||
Tracker.nonreactive(() => {
|
||||
Tracker.autorun(() => {
|
||||
if (handle.ready()) {
|
||||
this.getResults();
|
||||
this.searching.set(false);
|
||||
this.hasResults.set(true);
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
nextPage() {
|
||||
const sessionData = this.getSessionData();
|
||||
|
||||
this.autorun(() => {
|
||||
const handle = Meteor.subscribe('nextPage', sessionData.sessionId);
|
||||
Tracker.nonreactive(() => {
|
||||
Tracker.autorun(() => {
|
||||
if (handle.ready()) {
|
||||
this.getResults();
|
||||
this.searching.set(false);
|
||||
this.hasResults.set(true);
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
previousPage() {
|
||||
const sessionData = this.getSessionData();
|
||||
|
||||
this.autorun(() => {
|
||||
const handle = Meteor.subscribe('previousPage', sessionData.sessionId);
|
||||
Tracker.nonreactive(() => {
|
||||
Tracker.autorun(() => {
|
||||
if (handle.ready()) {
|
||||
this.getResults();
|
||||
this.searching.set(false);
|
||||
this.hasResults.set(true);
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
getResultsHeading() {
|
||||
if (this.resultsCount === 0) {
|
||||
return TAPi18n.__('no-cards-found');
|
||||
} else if (this.resultsCount === 1) {
|
||||
return TAPi18n.__('one-card-found');
|
||||
} else if (this.resultsCount === this.totalHits) {
|
||||
return TAPi18n.__('n-cards-found', this.resultsCount);
|
||||
}
|
||||
|
||||
return TAPi18n.__('n-n-of-n-cards-found', {
|
||||
start: this.resultsStart,
|
||||
end: this.resultsEnd,
|
||||
total: this.totalHits,
|
||||
});
|
||||
},
|
||||
|
||||
getSearchHref() {
|
||||
const baseUrl = window.location.href.replace(/([?#].*$|\s*$)/, '');
|
||||
return `${baseUrl}?q=${encodeURIComponent(this.query.get())}`;
|
||||
},
|
||||
this.autorunGlobalSearch(params);
|
||||
}
|
||||
|
||||
searchInstructions() {
|
||||
tags = {
|
||||
const tags = {
|
||||
operator_board: TAPi18n.__('operator-board'),
|
||||
operator_list: TAPi18n.__('operator-list'),
|
||||
operator_swimlane: TAPi18n.__('operator-swimlane'),
|
||||
|
|
@ -654,7 +513,7 @@ BlazeComponent.extendComponent({
|
|||
[
|
||||
'globalSearch-instructions-operator-has',
|
||||
'globalSearch-instructions-operator-sort',
|
||||
'globalSearch-instructions-operator-limit'
|
||||
'globalSearch-instructions-operator-limit',
|
||||
].forEach(instruction => {
|
||||
text += `\n* ${TAPi18n.__(instruction, tags)}`;
|
||||
});
|
||||
|
|
@ -672,7 +531,7 @@ BlazeComponent.extendComponent({
|
|||
});
|
||||
|
||||
return text;
|
||||
},
|
||||
}
|
||||
|
||||
labelColors() {
|
||||
return Boards.simpleSchema()._schema['labels.$.color'].allowedValues.map(
|
||||
|
|
@ -680,23 +539,16 @@ BlazeComponent.extendComponent({
|
|||
return { color, name: TAPi18n.__(`color-${color}`) };
|
||||
},
|
||||
);
|
||||
},
|
||||
}
|
||||
|
||||
events() {
|
||||
return [
|
||||
{
|
||||
...super.events()[0],
|
||||
'submit .js-search-query-form'(evt) {
|
||||
evt.preventDefault();
|
||||
this.searchAllBoards(evt.target.searchQuery.value);
|
||||
},
|
||||
'click .js-next-page'(evt) {
|
||||
evt.preventDefault();
|
||||
this.nextPage();
|
||||
},
|
||||
'click .js-previous-page'(evt) {
|
||||
evt.preventDefault();
|
||||
this.previousPage();
|
||||
},
|
||||
'click .js-label-color'(evt) {
|
||||
evt.preventDefault();
|
||||
const input = document.getElementById('global-search-input');
|
||||
|
|
@ -739,5 +591,7 @@ BlazeComponent.extendComponent({
|
|||
},
|
||||
},
|
||||
];
|
||||
},
|
||||
}).register('globalSearch');
|
||||
}
|
||||
}
|
||||
|
||||
GlobalSearchComponent.register('globalSearch');
|
||||
|
|
|
|||
|
|
@ -24,14 +24,16 @@ template(name="myCardsModalTitle")
|
|||
|
||||
template(name="myCards")
|
||||
if currentUser
|
||||
if isPageReady.get
|
||||
if searching.get
|
||||
+spinner
|
||||
else
|
||||
.wrapper
|
||||
if $eq myCardsSort 'board'
|
||||
each board in myCardsList
|
||||
.my-cards-board-wrapper
|
||||
.my-cards-board-title(class=board.colorClass, id="header")
|
||||
a(href=board.absoluteUrl)
|
||||
+viewer
|
||||
+viewer
|
||||
= board.title
|
||||
each swimlane in board.mySwimlanes
|
||||
.my-cards-swimlane-title(class="{{#if swimlane.colorClass}}{{ swimlane.colorClass }}{{else}}swimlane-default-color{{/if}}")
|
||||
|
|
@ -50,8 +52,6 @@ template(name="myCards")
|
|||
.my-cards-dueat-list-wrapper
|
||||
each card in myDueCardsList
|
||||
+resultCard(card)
|
||||
else
|
||||
+spinner
|
||||
|
||||
template(name="myCardsSortChangePopup")
|
||||
if currentUser
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
import {CardSearchPagedComponent} from "../../lib/cardSearch";
|
||||
|
||||
const subManager = new SubsManager();
|
||||
|
||||
BlazeComponent.extendComponent({
|
||||
|
|
@ -42,177 +44,139 @@ BlazeComponent.extendComponent({
|
|||
},
|
||||
}).register('myCardsSortChangePopup');
|
||||
|
||||
BlazeComponent.extendComponent({
|
||||
class MyCardsComponent extends CardSearchPagedComponent {
|
||||
onCreated() {
|
||||
this.isPageReady = new ReactiveVar(false);
|
||||
super.onCreated();
|
||||
|
||||
this.autorun(() => {
|
||||
const handle = subManager.subscribe('myCards');
|
||||
Tracker.nonreactive(() => {
|
||||
Tracker.autorun(() => {
|
||||
this.isPageReady.set(handle.ready());
|
||||
});
|
||||
});
|
||||
});
|
||||
const queryParams = {
|
||||
users: [Meteor.user().username],
|
||||
sort: { name: 'dueAt', order: 'des' },
|
||||
};
|
||||
|
||||
this.autorunGlobalSearch(queryParams);
|
||||
Meteor.subscribe('setting');
|
||||
},
|
||||
}
|
||||
|
||||
myCardsSort() {
|
||||
// eslint-disable-next-line no-console
|
||||
//console.log('sort:', Utils.myCardsSort());
|
||||
return Utils.myCardsSort();
|
||||
},
|
||||
}
|
||||
|
||||
sortByBoard() {
|
||||
return this.myCardsSort() === 'board';
|
||||
},
|
||||
}
|
||||
|
||||
myCardsList() {
|
||||
const userId = Meteor.userId();
|
||||
const boards = [];
|
||||
let board = null;
|
||||
let swimlane = null;
|
||||
let list = null;
|
||||
|
||||
const cursor = Cards.find(
|
||||
{
|
||||
$or: [{ members: userId }, { assignees: userId }],
|
||||
archived: false,
|
||||
},
|
||||
{
|
||||
sort: {
|
||||
boardId: 1,
|
||||
swimlaneId: 1,
|
||||
listId: 1,
|
||||
sort: 1,
|
||||
},
|
||||
},
|
||||
);
|
||||
const cursor = this.getResults();
|
||||
|
||||
let newBoard = false;
|
||||
let newSwimlane = false;
|
||||
let newList = false;
|
||||
if (cursor) {
|
||||
let newBoard = false;
|
||||
let newSwimlane = false;
|
||||
let newList = false;
|
||||
|
||||
cursor.forEach(card => {
|
||||
// eslint-disable-next-line no-console
|
||||
// console.log('card:', card.title);
|
||||
if (list === null || card.listId !== list._id) {
|
||||
cursor.forEach(card => {
|
||||
// eslint-disable-next-line no-console
|
||||
// console.log('new list');
|
||||
list = card.getList();
|
||||
if (list.archived) {
|
||||
list = null;
|
||||
return;
|
||||
// console.log('card:', card.title);
|
||||
if (list === null || card.listId !== list._id) {
|
||||
// eslint-disable-next-line no-console
|
||||
// console.log('new list');
|
||||
list = card.getList();
|
||||
if (list.archived) {
|
||||
list = null;
|
||||
return;
|
||||
}
|
||||
list.myCards = [card];
|
||||
newList = true;
|
||||
}
|
||||
list.myCards = [card];
|
||||
newList = true;
|
||||
}
|
||||
if (swimlane === null || card.swimlaneId !== swimlane._id) {
|
||||
// eslint-disable-next-line no-console
|
||||
// console.log('new swimlane');
|
||||
swimlane = card.getSwimlane();
|
||||
if (swimlane.archived) {
|
||||
swimlane = null;
|
||||
return;
|
||||
if (swimlane === null || card.swimlaneId !== swimlane._id) {
|
||||
// eslint-disable-next-line no-console
|
||||
// console.log('new swimlane');
|
||||
swimlane = card.getSwimlane();
|
||||
if (swimlane.archived) {
|
||||
swimlane = null;
|
||||
return;
|
||||
}
|
||||
swimlane.myLists = [list];
|
||||
newSwimlane = true;
|
||||
}
|
||||
swimlane.myLists = [list];
|
||||
newSwimlane = true;
|
||||
}
|
||||
if (board === null || card.boardId !== board._id) {
|
||||
// eslint-disable-next-line no-console
|
||||
// console.log('new board');
|
||||
board = card.getBoard();
|
||||
if (board.archived) {
|
||||
board = null;
|
||||
return;
|
||||
if (board === null || card.boardId !== board._id) {
|
||||
// eslint-disable-next-line no-console
|
||||
// console.log('new board');
|
||||
board = card.getBoard();
|
||||
if (board.archived) {
|
||||
board = null;
|
||||
return;
|
||||
}
|
||||
// eslint-disable-next-line no-console
|
||||
// console.log('board:', b, b._id, b.title);
|
||||
board.mySwimlanes = [swimlane];
|
||||
newBoard = true;
|
||||
}
|
||||
// eslint-disable-next-line no-console
|
||||
// console.log('board:', b, b._id, b.title);
|
||||
board.mySwimlanes = [swimlane];
|
||||
newBoard = true;
|
||||
}
|
||||
|
||||
if (newBoard) {
|
||||
boards.push(board);
|
||||
} else if (newSwimlane) {
|
||||
board.mySwimlanes.push(swimlane);
|
||||
} else if (newList) {
|
||||
swimlane.myLists.push(list);
|
||||
} else {
|
||||
list.myCards.push(card);
|
||||
}
|
||||
if (newBoard) {
|
||||
boards.push(board);
|
||||
} else if (newSwimlane) {
|
||||
board.mySwimlanes.push(swimlane);
|
||||
} else if (newList) {
|
||||
swimlane.myLists.push(list);
|
||||
} else {
|
||||
list.myCards.push(card);
|
||||
}
|
||||
|
||||
newBoard = false;
|
||||
newSwimlane = false;
|
||||
newList = false;
|
||||
});
|
||||
newBoard = false;
|
||||
newSwimlane = false;
|
||||
newList = false;
|
||||
});
|
||||
|
||||
// sort the data structure
|
||||
boards.forEach(board => {
|
||||
board.mySwimlanes.forEach(swimlane => {
|
||||
swimlane.myLists.forEach(list => {
|
||||
list.myCards.sort((a, b) => {
|
||||
// sort the data structure
|
||||
boards.forEach(board => {
|
||||
board.mySwimlanes.forEach(swimlane => {
|
||||
swimlane.myLists.forEach(list => {
|
||||
list.myCards.sort((a, b) => {
|
||||
return a.sort - b.sort;
|
||||
});
|
||||
});
|
||||
swimlane.myLists.sort((a, b) => {
|
||||
return a.sort - b.sort;
|
||||
});
|
||||
});
|
||||
swimlane.myLists.sort((a, b) => {
|
||||
board.mySwimlanes.sort((a, b) => {
|
||||
return a.sort - b.sort;
|
||||
});
|
||||
});
|
||||
board.mySwimlanes.sort((a, b) => {
|
||||
return a.sort - b.sort;
|
||||
|
||||
boards.sort((a, b) => {
|
||||
let x = a.sort;
|
||||
let y = b.sort;
|
||||
|
||||
// show the template board last
|
||||
if (a.type === 'template-container') {
|
||||
x = 99999999;
|
||||
} else if (b.type === 'template-container') {
|
||||
y = 99999999;
|
||||
}
|
||||
return x - y;
|
||||
});
|
||||
});
|
||||
|
||||
boards.sort((a, b) => {
|
||||
let x = a.sort;
|
||||
let y = b.sort;
|
||||
// eslint-disable-next-line no-console
|
||||
// console.log('boards:', boards);
|
||||
return boards;
|
||||
}
|
||||
|
||||
// show the template board last
|
||||
if (a.type === 'template-container') {
|
||||
x = 99999999;
|
||||
} else if (b.type === 'template-container') {
|
||||
y = 99999999;
|
||||
}
|
||||
return x - y;
|
||||
});
|
||||
|
||||
// eslint-disable-next-line no-console
|
||||
// console.log('boards:', boards);
|
||||
return boards;
|
||||
},
|
||||
return [];
|
||||
}
|
||||
|
||||
myDueCardsList() {
|
||||
const userId = Meteor.userId();
|
||||
|
||||
const cursor = Cards.find(
|
||||
{
|
||||
$or: [{ members: userId }, { assignees: userId }],
|
||||
archived: false,
|
||||
},
|
||||
{
|
||||
sort: {
|
||||
dueAt: -1,
|
||||
boardId: 1,
|
||||
swimlaneId: 1,
|
||||
listId: 1,
|
||||
sort: 1,
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
// eslint-disable-next-line no-console
|
||||
// console.log('cursor:', cursor);
|
||||
|
||||
const cursor = this.getResults();
|
||||
const cards = [];
|
||||
cursor.forEach(card => {
|
||||
if (
|
||||
!card.getBoard().archived &&
|
||||
!card.getSwimlane().archived &&
|
||||
!card.getList().archived
|
||||
) {
|
||||
cards.push(card);
|
||||
}
|
||||
cards.push(card);
|
||||
});
|
||||
|
||||
cards.sort((a, b) => {
|
||||
|
|
@ -228,23 +192,6 @@ BlazeComponent.extendComponent({
|
|||
// eslint-disable-next-line no-console
|
||||
// console.log('cursor:', cards);
|
||||
return cards;
|
||||
},
|
||||
|
||||
events() {
|
||||
return [
|
||||
{
|
||||
// 'click .js-my-card'(evt) {
|
||||
// const card = this.currentData().card;
|
||||
// // eslint-disable-next-line no-console
|
||||
// console.log('currentData():', this.currentData());
|
||||
// // eslint-disable-next-line no-console
|
||||
// console.log('card:', card);
|
||||
// if (card) {
|
||||
// Utils.goCardId(card._id);
|
||||
// }
|
||||
// evt.preventDefault();
|
||||
// },
|
||||
},
|
||||
];
|
||||
},
|
||||
}).register('myCards');
|
||||
}
|
||||
}
|
||||
MyCardsComponent.register('myCards');
|
||||
|
|
|
|||
|
|
@ -5,175 +5,38 @@ Meteor.publish('card', cardId => {
|
|||
return Cards.find({ _id: cardId });
|
||||
});
|
||||
|
||||
Meteor.publish('myCards', function() {
|
||||
const userId = Meteor.userId();
|
||||
Meteor.publish('myCards', function(sessionId) {
|
||||
|
||||
const archivedBoards = [];
|
||||
Boards.find({ archived: true }).forEach(board => {
|
||||
archivedBoards.push(board._id);
|
||||
});
|
||||
|
||||
const archivedSwimlanes = [];
|
||||
Swimlanes.find({ archived: true }).forEach(swimlane => {
|
||||
archivedSwimlanes.push(swimlane._id);
|
||||
});
|
||||
|
||||
const archivedLists = [];
|
||||
Lists.find({ archived: true }).forEach(list => {
|
||||
archivedLists.push(list._id);
|
||||
});
|
||||
|
||||
selector = {
|
||||
archived: false,
|
||||
boardId: { $nin: archivedBoards },
|
||||
swimlaneId: { $nin: archivedSwimlanes },
|
||||
listId: { $nin: archivedLists },
|
||||
$or: [{ members: userId }, { assignees: userId }],
|
||||
const queryParams = {
|
||||
users: [Meteor.user().username],
|
||||
// limit: 25,
|
||||
skip: 0,
|
||||
// sort: { name: 'dueAt', order: 'des' },
|
||||
};
|
||||
|
||||
const cards = Cards.find(selector, {
|
||||
fields: {
|
||||
_id: 1,
|
||||
archived: 1,
|
||||
boardId: 1,
|
||||
swimlaneId: 1,
|
||||
listId: 1,
|
||||
title: 1,
|
||||
type: 1,
|
||||
sort: 1,
|
||||
members: 1,
|
||||
assignees: 1,
|
||||
colors: 1,
|
||||
dueAt: 1,
|
||||
},
|
||||
});
|
||||
|
||||
const boards = [];
|
||||
const swimlanes = [];
|
||||
const lists = [];
|
||||
const users = [];
|
||||
|
||||
cards.forEach(card => {
|
||||
if (card.boardId) boards.push(card.boardId);
|
||||
if (card.swimlaneId) swimlanes.push(card.swimlaneId);
|
||||
if (card.listId) lists.push(card.listId);
|
||||
if (card.members) {
|
||||
card.members.forEach(userId => {
|
||||
users.push(userId);
|
||||
});
|
||||
}
|
||||
if (card.assignees) {
|
||||
card.assignees.forEach(userId => {
|
||||
users.push(userId);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
return [
|
||||
cards,
|
||||
Boards.find({ _id: { $in: boards } }),
|
||||
Swimlanes.find({ _id: { $in: swimlanes } }),
|
||||
Lists.find({ _id: { $in: lists } }),
|
||||
Users.find({ _id: { $in: users } }, { fields: Users.safeFields }),
|
||||
];
|
||||
return buildQuery(sessionId, queryParams);
|
||||
});
|
||||
|
||||
Meteor.publish('dueCards', function(allUsers = false) {
|
||||
check(allUsers, Boolean);
|
||||
|
||||
// eslint-disable-next-line no-console
|
||||
// console.log('all users:', allUsers);
|
||||
|
||||
const user = Users.findOne({ _id: this.userId });
|
||||
|
||||
const archivedBoards = [];
|
||||
Boards.find({ archived: true }).forEach(board => {
|
||||
archivedBoards.push(board._id);
|
||||
});
|
||||
|
||||
const permiitedBoards = [];
|
||||
let selector = {
|
||||
archived: false,
|
||||
};
|
||||
|
||||
selector.$or = [
|
||||
{ permission: 'public' },
|
||||
{ members: { $elemMatch: { userId: user._id, isActive: true } } },
|
||||
];
|
||||
|
||||
Boards.find(selector).forEach(board => {
|
||||
permiitedBoards.push(board._id);
|
||||
});
|
||||
|
||||
const archivedSwimlanes = [];
|
||||
Swimlanes.find({ archived: true }).forEach(swimlane => {
|
||||
archivedSwimlanes.push(swimlane._id);
|
||||
});
|
||||
|
||||
const archivedLists = [];
|
||||
Lists.find({ archived: true }).forEach(list => {
|
||||
archivedLists.push(list._id);
|
||||
});
|
||||
|
||||
selector = {
|
||||
archived: false,
|
||||
boardId: { $nin: archivedBoards, $in: permiitedBoards },
|
||||
swimlaneId: { $nin: archivedSwimlanes },
|
||||
listId: { $nin: archivedLists },
|
||||
dueAt: { $ne: null },
|
||||
endAt: null,
|
||||
};
|
||||
|
||||
if (!allUsers) {
|
||||
selector.$or = [{ members: user._id }, { assignees: user._id }];
|
||||
}
|
||||
|
||||
const cards = Cards.find(selector, {
|
||||
fields: {
|
||||
_id: 1,
|
||||
archived: 1,
|
||||
boardId: 1,
|
||||
swimlaneId: 1,
|
||||
listId: 1,
|
||||
title: 1,
|
||||
type: 1,
|
||||
sort: 1,
|
||||
members: 1,
|
||||
assignees: 1,
|
||||
colors: 1,
|
||||
dueAt: 1,
|
||||
},
|
||||
});
|
||||
|
||||
const boards = [];
|
||||
const swimlanes = [];
|
||||
const lists = [];
|
||||
const users = [];
|
||||
|
||||
cards.forEach(card => {
|
||||
if (card.boardId) boards.push(card.boardId);
|
||||
if (card.swimlaneId) swimlanes.push(card.swimlaneId);
|
||||
if (card.listId) lists.push(card.listId);
|
||||
if (card.members) {
|
||||
card.members.forEach(userId => {
|
||||
users.push(userId);
|
||||
});
|
||||
}
|
||||
if (card.assignees) {
|
||||
card.assignees.forEach(userId => {
|
||||
users.push(userId);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
return [
|
||||
cards,
|
||||
Boards.find({ _id: { $in: boards } }),
|
||||
Swimlanes.find({ _id: { $in: swimlanes } }),
|
||||
Lists.find({ _id: { $in: lists } }),
|
||||
Users.find({ _id: { $in: users } }, { fields: Users.safeFields }),
|
||||
];
|
||||
});
|
||||
// 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 = [Meteor.user().username];
|
||||
// }
|
||||
//
|
||||
// return buildQuery(sessionId, queryParams);
|
||||
// });
|
||||
|
||||
Meteor.publish('globalSearch', function(sessionId, queryParams) {
|
||||
check(sessionId, String);
|
||||
|
|
@ -182,9 +45,11 @@ Meteor.publish('globalSearch', function(sessionId, queryParams) {
|
|||
// eslint-disable-next-line no-console
|
||||
// console.log('queryParams:', queryParams);
|
||||
|
||||
return buildQuery(sessionId, queryParams);
|
||||
});
|
||||
|
||||
function buildQuery(sessionId, queryParams) {
|
||||
const userId = Meteor.userId();
|
||||
// eslint-disable-next-line no-console
|
||||
// console.log('userId:', userId);
|
||||
|
||||
const errors = new (class {
|
||||
constructor() {
|
||||
|
|
@ -267,7 +132,7 @@ Meteor.publish('globalSearch', function(sessionId, queryParams) {
|
|||
|
||||
let archived = false;
|
||||
let endAt = null;
|
||||
if (queryParams.status.length) {
|
||||
if (queryParams.status && queryParams.status.length) {
|
||||
queryParams.status.forEach(status => {
|
||||
if (status === 'archived') {
|
||||
archived = true;
|
||||
|
|
@ -320,7 +185,7 @@ Meteor.publish('globalSearch', function(sessionId, queryParams) {
|
|||
selector.endAt = endAt;
|
||||
}
|
||||
|
||||
if (queryParams.boards.length) {
|
||||
if (queryParams.boards && queryParams.boards.length) {
|
||||
const queryBoards = [];
|
||||
queryParams.boards.forEach(query => {
|
||||
const boards = Boards.userSearch(userId, {
|
||||
|
|
@ -338,7 +203,7 @@ Meteor.publish('globalSearch', function(sessionId, queryParams) {
|
|||
selector.boardId.$in = queryBoards;
|
||||
}
|
||||
|
||||
if (queryParams.swimlanes.length) {
|
||||
if (queryParams.swimlanes && queryParams.swimlanes.length) {
|
||||
const querySwimlanes = [];
|
||||
queryParams.swimlanes.forEach(query => {
|
||||
const swimlanes = Swimlanes.find({
|
||||
|
|
@ -360,7 +225,7 @@ Meteor.publish('globalSearch', function(sessionId, queryParams) {
|
|||
selector.swimlaneId.$in = querySwimlanes;
|
||||
}
|
||||
|
||||
if (queryParams.lists.length) {
|
||||
if (queryParams.lists && queryParams.lists.length) {
|
||||
const queryLists = [];
|
||||
queryParams.lists.forEach(query => {
|
||||
const lists = Lists.find({
|
||||
|
|
@ -382,7 +247,7 @@ Meteor.publish('globalSearch', function(sessionId, queryParams) {
|
|||
selector.listId.$in = queryLists;
|
||||
}
|
||||
|
||||
if (queryParams.comments.length) {
|
||||
if (queryParams.comments && queryParams.comments.length) {
|
||||
const cardIds = CardComments.textSearch(userId, queryParams.comments).map(
|
||||
com => {
|
||||
return com.cardId;
|
||||
|
|
@ -398,15 +263,15 @@ Meteor.publish('globalSearch', function(sessionId, queryParams) {
|
|||
['dueAt', 'createdAt', 'modifiedAt'].forEach(field => {
|
||||
if (queryParams[field]) {
|
||||
selector[field] = {};
|
||||
selector[field][queryParams[field]['operator']] = new Date(
|
||||
queryParams[field]['value'],
|
||||
selector[field][queryParams[field].operator] = new Date(
|
||||
queryParams[field].value,
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
const queryMembers = [];
|
||||
const queryAssignees = [];
|
||||
if (queryParams.users.length) {
|
||||
if (queryParams.users && queryParams.users.length) {
|
||||
queryParams.users.forEach(query => {
|
||||
const users = Users.find({
|
||||
username: query,
|
||||
|
|
@ -422,7 +287,7 @@ Meteor.publish('globalSearch', function(sessionId, queryParams) {
|
|||
});
|
||||
}
|
||||
|
||||
if (queryParams.members.length) {
|
||||
if (queryParams.members && queryParams.members.length) {
|
||||
queryParams.members.forEach(query => {
|
||||
const users = Users.find({
|
||||
username: query,
|
||||
|
|
@ -437,7 +302,7 @@ Meteor.publish('globalSearch', function(sessionId, queryParams) {
|
|||
});
|
||||
}
|
||||
|
||||
if (queryParams.assignees.length) {
|
||||
if (queryParams.assignees && queryParams.assignees.length) {
|
||||
queryParams.assignees.forEach(query => {
|
||||
const users = Users.find({
|
||||
username: query,
|
||||
|
|
@ -465,7 +330,7 @@ Meteor.publish('globalSearch', function(sessionId, queryParams) {
|
|||
selector.assignees = { $in: queryAssignees };
|
||||
}
|
||||
|
||||
if (queryParams.labels.length) {
|
||||
if (queryParams.labels && queryParams.labels.length) {
|
||||
queryParams.labels.forEach(label => {
|
||||
const queryLabels = [];
|
||||
|
||||
|
|
@ -516,16 +381,26 @@ Meteor.publish('globalSearch', function(sessionId, queryParams) {
|
|||
});
|
||||
}
|
||||
|
||||
if (queryParams.has.length) {
|
||||
if (queryParams.has && queryParams.has.length) {
|
||||
queryParams.has.forEach(has => {
|
||||
switch (has.field) {
|
||||
case 'attachment':
|
||||
const attachments = Attachments.find({}, { fields: { cardId: 1 } });
|
||||
selector.$and.push({ _id: { $in: attachments.map(a => a.cardId) } });
|
||||
selector.$and.push({
|
||||
_id: {
|
||||
$in: Attachments.find({}, { fields: { cardId: 1 } }).map(
|
||||
a => a.cardId,
|
||||
),
|
||||
},
|
||||
});
|
||||
break;
|
||||
case 'checklist':
|
||||
const checklists = Checklists.find({}, { fields: { cardId: 1 } });
|
||||
selector.$and.push({ _id: { $in: checklists.map(a => a.cardId) } });
|
||||
selector.$and.push({
|
||||
_id: {
|
||||
$in: Checklists.find({}, { fields: { cardId: 1 } }).map(
|
||||
a => a.cardId,
|
||||
),
|
||||
},
|
||||
});
|
||||
break;
|
||||
case 'description':
|
||||
case 'startAt':
|
||||
|
|
@ -677,7 +552,7 @@ Meteor.publish('globalSearch', function(sessionId, queryParams) {
|
|||
// console.log('projection:', projection);
|
||||
|
||||
return findCards(sessionId, selector, projection, errors);
|
||||
});
|
||||
}
|
||||
|
||||
Meteor.publish('brokenCards', function() {
|
||||
const user = Users.findOne({ _id: this.userId });
|
||||
|
|
@ -773,7 +648,8 @@ function findCards(sessionId, selector, projection, errors = null) {
|
|||
const userId = Meteor.userId();
|
||||
|
||||
// eslint-disable-next-line no-console
|
||||
// console.log('selector:', selector);
|
||||
console.log('selector:', selector);
|
||||
console.log('selector.$and:', selector.$and);
|
||||
// eslint-disable-next-line no-console
|
||||
// console.log('projection:', projection);
|
||||
let cards;
|
||||
|
|
@ -879,5 +755,5 @@ function findCards(sessionId, selector, projection, errors = null) {
|
|||
];
|
||||
}
|
||||
|
||||
return [SessionData.find({ userId: userId, sessionId })];
|
||||
return [SessionData.find({ userId, sessionId })];
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue