diff --git a/client/components/main/globalSearch.jade b/client/components/main/globalSearch.jade index 1459fee7a..c288cca22 100644 --- a/client/components/main/globalSearch.jade +++ b/client/components/main/globalSearch.jade @@ -19,6 +19,11 @@ template(name="globalSearch") h1 = resultsCount.get | Results + if queryErrors.get + div + each msg in errorMessages + span.global-search-error-messages + | {{_ msg.tag msg.value }} each card in results .global-search-card-wrapper a.minicard-wrapper.card-title(href=card.absoluteUrl) diff --git a/client/components/main/globalSearch.js b/client/components/main/globalSearch.js index b5003e97a..f2b775d86 100644 --- a/client/components/main/globalSearch.js +++ b/client/components/main/globalSearch.js @@ -42,33 +42,50 @@ BlazeComponent.extendComponent({ this.query = new ReactiveVar(''); this.queryParams = null; this.resultsCount = new ReactiveVar(0); - - // this.autorun(() => { - // const handle = subManager.subscribe('globalSearch'); - // Tracker.nonreactive(() => { - // Tracker.autorun(() => { - // this.isPageReady.set(handle.ready()); - // }); - // }); - // }); + this.queryErrors = new ReactiveVar(null); Meteor.subscribe('setting'); }, results() { if (this.queryParams) { - const cards = Cards.globalSearch(this.queryParams); - this.resultsCount.set(cards.count()); - return cards; + const results = Cards.globalSearch(this.queryParams); + // eslint-disable-next-line no-console + console.log('errors:', results.errors); + this.resultsCount.set(results.cards.count()); + this.queryErrors.set(results.errors); + return results.cards; } + this.resultsCount.set(0); return []; }, + errorMessages() { + const errors = this.queryErrors.get(); + const messages = []; + + errors.notFound.boards.forEach(board => { + messages.push({ tag: 'board-title-not-found', value: board }); + }); + errors.notFound.swimlanes.forEach(swim => { + messages.push({ tag: 'swimlane-title-not-found', value: swim }); + }); + errors.notFound.lists.forEach(list => { + messages.push({ tag: 'list-title-not-found', value: list }); + }); + errors.notFound.users.forEach(user => { + messages.push({ tag: 'user-username-not-found', value: user }); + }); + + return messages; + }, + events() { return [ { 'submit .js-search-query-form'(evt) { evt.preventDefault(); this.query.set(evt.target.searchQuery.value); + this.queryErrors.set(null); if (!this.query.get()) { this.searching.set(false); diff --git a/client/components/main/globalSearch.styl b/client/components/main/globalSearch.styl index 42d41e079..7afcaaa04 100644 --- a/client/components/main/globalSearch.styl +++ b/client/components/main/globalSearch.styl @@ -67,3 +67,6 @@ .global-search-context-list margin-bottom: 0.7rem + +.global-search-error-messages + color: darkred diff --git a/i18n/en.i18n.json b/i18n/en.i18n.json index a81a8c345..4ef083749 100644 --- a/i18n/en.i18n.json +++ b/i18n/en.i18n.json @@ -861,5 +861,9 @@ "dueCardsViewChange-title": "Due Cards View", "dueCardsViewChange-choice-me": "Me", "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.", + "board-title-not-found": "Board '%s' not found.", + "swimlane-title-not-found": "Swimlane '%s' not found.", + "list-title-not-found": "List '%s' not found.", + "user-username-not-found": "Username '%s' not found." } diff --git a/models/boards.js b/models/boards.js index 8a286dc8e..b9c5460d4 100644 --- a/models/boards.js +++ b/models/boards.js @@ -1208,6 +1208,20 @@ function boardRemover(userId, doc) { ); } +Boards.userSearch = (userId, includeArchived = false, selector = {}) => { + if (!includeArchived) { + selector = { + archived: false, + }; + } + selector.$or = [ + { permission: 'public' }, + { members: { $elemMatch: { userId, isActive: true } } }, + ]; + + return Boards.find(selector); +}; + Boards.userBoards = (userId, includeArchived = false, selector = {}) => { if (!includeArchived) { selector = { diff --git a/models/cards.js b/models/cards.js index e6848c646..ec2817e37 100644 --- a/models/cards.js +++ b/models/cards.js @@ -1735,6 +1735,15 @@ Cards.globalSearch = queryParams => { // eslint-disable-next-line no-console console.log('userId:', this.userId); + const errors = { + notFound: { + boards: [], + swimlanes: [], + lists: [], + users: [], + }, + }; + const selector = { archived: false, type: 'cardType-card', @@ -1743,58 +1752,78 @@ Cards.globalSearch = queryParams => { listId: { $nin: Lists.archivedListIds() }, }; - if ('swimlanesSelector' in queryParams) { - const swimSelector = { - archived: false, - }; - - for (const key in queryParams.swimlanesSelector) { - swimSelector[key] = queryParams.swimlanesSelector[key]; - } - - selector.swimlaneId.$in = Swimlanes.find(swimSelector).map(swim => { - return swim._id; + if (queryParams.boards.length) { + const queryBoards = []; + queryParams.boards.forEach(query => { + const boards = Boards.userSearch(userId, { + title: query, + }); + if (boards.count()) { + boards.forEach(board => { + queryBoards.push(board._id); + }); + } else { + errors.notFound.boards.push(query); + } }); + + selector.boardId.$in = queryBoards; } - if ('listsSelector' in queryParams) { - const listsSelector = { - archived: false, - }; - - // eslint-disable-next-line no-console - // console.log('listsSelector:', queryParams.listsSelector.keys()); - for (const key in queryParams.listsSelector) { - listsSelector[key] = queryParams.listsSelector[key]; - } - - // eslint-disable-next-line no-console - console.log('search list selector:', selector); - selector.listId.$in = Lists.find(listsSelector).map(list => { - return list._id; + if (queryParams.swimlanes.length) { + const querySwimlanes = []; + queryParams.swimlanes.forEach(query => { + const swimlanes = Swimlanes.find({ + title: query, + }); + if (swimlanes.count()) { + swimlanes.forEach(swim => { + querySwimlanes.push(swim._id); + }); + } else { + errors.notFound.swimlanes.push(query); + } }); + + selector.swimlaneId.$in = querySwimlanes; + } + + if (queryParams.lists.length) { + const queryLists = []; + queryParams.lists.forEach(query => { + const lists = Lists.find({ + title: query, + }); + if (lists.count()) { + lists.forEach(list => { + queryLists.push(list._id); + }); + } else { + errors.notFound.lists.push(query); + } + }); + + selector.listId.$in = queryLists; } if (queryParams.users.length) { - const users = []; - Users.find({ username: { $in: queryParams.users } }).forEach(user => { - users.push(user._id); + const queryUsers = []; + queryParams.users.forEach(query => { + const users = Users.find({ + username: query, + }); + if (users.count()) { + users.forEach(user => { + queryUsers.push(user._id); + }); + } else { + errors.notFound.users.push(query); + } }); - if (users.length) { - selector.$or = [ - { members: { $in: users } }, - { assignees: { $in: users } }, - ]; - } - } - - if (queryParams.text) { - const regex = new RegExp(queryParams.text, 'i'); selector.$or = [ - { title: regex }, - { description: regex }, - { customFields: { $elemMatch: { value: regex } } }, + { members: { $in: queryUsers } }, + { assignees: { $in: queryUsers } }, ]; } @@ -1820,7 +1849,7 @@ Cards.globalSearch = queryParams => { // eslint-disable-next-line no-console console.log('count:', cards.count()); - return cards; + return { cards, errors }; }; //FUNCTIONS FOR creation of Activities diff --git a/server/publications/cards.js b/server/publications/cards.js index 6d14cf02c..802f47464 100644 --- a/server/publications/cards.js +++ b/server/publications/cards.js @@ -181,7 +181,7 @@ Meteor.publish('globalSearch', function(queryParams) { // eslint-disable-next-line no-console console.log('queryParams:', queryParams); - const cards = Cards.globalSearch(queryParams); + const cards = Cards.globalSearch(queryParams).cards; const boards = []; const swimlanes = [];