diff --git a/client/components/main/globalSearch.js b/client/components/main/globalSearch.js index d04eedaa7..fbc61c37f 100644 --- a/client/components/main/globalSearch.js +++ b/client/components/main/globalSearch.js @@ -1,5 +1,33 @@ import { CardSearchPagedComponent } from '../../lib/cardSearch'; import moment from 'moment'; +import { + OPERATOR_BOARD, + OPERATOR_DUE, OPERATOR_LIST, + OPERATOR_SWIMLANE, OPERATOR_USER, + ORDER_ASCENDING, + ORDER_DESCENDING, + PREDICATE_ALL, + PREDICATE_ARCHIVED, + PREDICATE_ASSIGNEES, + PREDICATE_ATTACHMENT, + PREDICATE_CHECKLIST, + PREDICATE_CREATED_AT, + PREDICATE_DESCRIPTION, + PREDICATE_DUE_AT, + PREDICATE_END_AT, + PREDICATE_ENDED, + PREDICATE_MEMBERS, + PREDICATE_MODIFIED_AT, + PREDICATE_MONTH, + PREDICATE_OPEN, + PREDICATE_OVERDUE, + PREDICATE_PRIVATE, + PREDICATE_PUBLIC, + PREDICATE_QUARTER, + PREDICATE_START_AT, + PREDICATE_WEEK, + PREDICATE_YEAR, +} from '../../../config/search-const'; // const subManager = new SubsManager(); @@ -135,22 +163,22 @@ class GlobalSearchComponent extends CardSearchPagedComponent { const reNegatedOperator = new RegExp('^-(?.*)$'); const operators = { - 'operator-board': 'boards', - 'operator-board-abbrev': 'boards', - 'operator-swimlane': 'swimlanes', - 'operator-swimlane-abbrev': 'swimlanes', - 'operator-list': 'lists', - 'operator-list-abbrev': 'lists', + 'operator-board': OPERATOR_BOARD, + 'operator-board-abbrev': OPERATOR_BOARD, + 'operator-swimlane': OPERATOR_SWIMLANE, + 'operator-swimlane-abbrev': OPERATOR_SWIMLANE, + 'operator-list': OPERATOR_LIST, + 'operator-list-abbrev': OPERATOR_LIST, 'operator-label': 'labels', 'operator-label-abbrev': 'labels', - 'operator-user': 'users', - 'operator-user-abbrev': 'users', + 'operator-user': OPERATOR_USER, + 'operator-user-abbrev': OPERATOR_USER, 'operator-member': 'members', 'operator-member-abbrev': 'members', 'operator-assignee': 'assignees', 'operator-assignee-abbrev': 'assignees', 'operator-status': 'status', - 'operator-due': 'dueAt', + 'operator-due': OPERATOR_DUE, 'operator-created': 'createdAt', 'operator-modified': 'modifiedAt', 'operator-comment': 'comments', @@ -161,36 +189,36 @@ class GlobalSearchComponent extends CardSearchPagedComponent { const predicates = { due: { - 'predicate-overdue': 'overdue', + 'predicate-overdue': PREDICATE_OVERDUE, }, durations: { - 'predicate-week': 'week', - 'predicate-month': 'month', - 'predicate-quarter': 'quarter', - 'predicate-year': 'year', + 'predicate-week': PREDICATE_WEEK, + 'predicate-month': PREDICATE_MONTH, + 'predicate-quarter': PREDICATE_QUARTER, + 'predicate-year': PREDICATE_YEAR, }, status: { - 'predicate-archived': 'archived', - 'predicate-all': 'all', - 'predicate-open': 'open', - 'predicate-ended': 'ended', - 'predicate-public': 'public', - 'predicate-private': 'private', + 'predicate-archived': PREDICATE_ARCHIVED, + 'predicate-all': PREDICATE_ALL, + 'predicate-open': PREDICATE_OPEN, + 'predicate-ended': PREDICATE_ENDED, + 'predicate-public': PREDICATE_PUBLIC, + 'predicate-private': PREDICATE_PRIVATE, }, sorts: { - 'predicate-due': 'dueAt', - 'predicate-created': 'createdAt', - 'predicate-modified': 'modifiedAt', + 'predicate-due': PREDICATE_DUE_AT, + 'predicate-created': PREDICATE_CREATED_AT, + 'predicate-modified': PREDICATE_MODIFIED_AT, }, has: { - 'predicate-description': 'description', - 'predicate-checklist': 'checklist', - 'predicate-attachment': 'attachment', - 'predicate-start': 'startAt', - 'predicate-end': 'endAt', - 'predicate-due': 'dueAt', - 'predicate-assignee': 'assignees', - 'predicate-member': 'members', + 'predicate-description': PREDICATE_DESCRIPTION, + 'predicate-checklist': PREDICATE_CHECKLIST, + 'predicate-attachment': PREDICATE_ATTACHMENT, + 'predicate-start': PREDICATE_START_AT, + 'predicate-end': PREDICATE_END_AT, + 'predicate-due': PREDICATE_DUE_AT, + 'predicate-assignee': PREDICATE_ASSIGNEES, + 'predicate-member': PREDICATE_MEMBERS, }, }; const predicateTranslations = {}; @@ -212,20 +240,25 @@ class GlobalSearchComponent extends CardSearchPagedComponent { const params = { limit: this.resultsPerPage, - boards: [], - swimlanes: [], - lists: [], - users: [], + // boards: [], + // swimlanes: [], + // lists: [], + // users: [], members: [], assignees: [], labels: [], status: [], - dueAt: null, + // dueAt: null, createdAt: null, modifiedAt: null, comments: [], has: [], }; + params[OPERATOR_BOARD] = []; + params[OPERATOR_DUE] = null; + params[OPERATOR_LIST] = []; + params[OPERATOR_SWIMLANE] = []; + params[OPERATOR_USER] = []; let text = ''; while (query) { @@ -254,7 +287,9 @@ class GlobalSearchComponent extends CardSearchPagedComponent { value = this.colorMap[value]; // console.log('found color:', value); } - } else if (['dueAt', 'createdAt', 'modifiedAt'].includes(operator)) { + } else if ( + [OPERATOR_DUE, 'createdAt', 'modifiedAt'].includes(operator) + ) { const days = parseInt(value, 10); let duration = null; if (isNaN(days)) { @@ -263,7 +298,7 @@ class GlobalSearchComponent extends CardSearchPagedComponent { duration = predicateTranslations.durations[value]; let date = null; switch (duration) { - case 'week': + case PREDICATE_WEEK: // eslint-disable-next-line no-case-declarations const week = moment().week(); if (week === 52) { @@ -273,7 +308,7 @@ class GlobalSearchComponent extends CardSearchPagedComponent { date = moment(week + 1, 'W'); } break; - case 'month': + case PREDICATE_MONTH: // eslint-disable-next-line no-case-declarations const month = moment().month(); // .month() is zero indexed @@ -284,7 +319,7 @@ class GlobalSearchComponent extends CardSearchPagedComponent { date = moment(month + 2, 'M'); } break; - case 'quarter': + case PREDICATE_QUARTER: // eslint-disable-next-line no-case-declarations const quarter = moment().quarter(); if (quarter === 4) { @@ -294,7 +329,7 @@ class GlobalSearchComponent extends CardSearchPagedComponent { date = moment(quarter + 1, 'Q'); } break; - case 'year': + case PREDICATE_YEAR: date = moment(moment().year() + 1, 'YYYY'); break; } @@ -304,7 +339,7 @@ class GlobalSearchComponent extends CardSearchPagedComponent { value: date.format('YYYY-MM-DD'), }; } - } else if (operator === 'dueAt' && value === 'overdue') { + } else if (operator === 'dueAt' && value === PREDICATE_OVERDUE) { value = { operator: '$lt', value: moment().format('YYYY-MM-DD'), @@ -346,7 +381,7 @@ class GlobalSearchComponent extends CardSearchPagedComponent { } else { value = { name: predicateTranslations.sorts[value], - order: negated ? 'des' : 'asc', + order: negated ? ORDER_DESCENDING : ORDER_ASCENDING, }; } } else if (operator === 'status') { diff --git a/config/search-const.js b/config/search-const.js new file mode 100644 index 000000000..3bebf3490 --- /dev/null +++ b/config/search-const.js @@ -0,0 +1,30 @@ +export const OPERATOR_DUE = 'dueAt'; +export const OPERATOR_BOARD = 'board'; +export const OPERATOR_LABEL = 'label'; +export const OPERATOR_LIST = 'list'; +export const OPERATOR_SWIMLANE = 'swimlane'; +export const OPERATOR_USER = 'user'; +export const ORDER_ASCENDING = 'asc'; +export const ORDER_DESCENDING = 'des'; +export const PREDICATE_ALL = 'all'; +export const PREDICATE_ARCHIVED = 'archived'; +export const PREDICATE_ASSIGNEES = 'assignees'; +export const PREDICATE_ATTACHMENT = 'attachment'; +export const PREDICATE_CHECKLIST = 'checklist'; +export const PREDICATE_CREATED_AT = 'createdAt'; +export const PREDICATE_DESCRIPTION = 'description'; +export const PREDICATE_DUE_AT = 'dueAt'; +export const PREDICATE_END_AT = 'endAt'; +export const PREDICATE_ENDED = 'ended'; +export const PREDICATE_MEMBERS = 'members'; +export const PREDICATE_MODIFIED_AT = 'modifiedAt'; +export const PREDICATE_MONTH = 'month'; +export const PREDICATE_OPEN = 'open'; +export const PREDICATE_OVERDUE = 'overdue'; +export const PREDICATE_PRIVATE = 'private'; +export const PREDICATE_PUBLIC = 'public'; +export const PREDICATE_QUARTER = 'quarter'; +export const PREDICATE_START_AT = 'startAt'; +export const PREDICATE_SYSTEM = 'system'; +export const PREDICATE_WEEK = 'week'; +export const PREDICATE_YEAR = 'year'; diff --git a/server/publications/cards.js b/server/publications/cards.js index f6421e988..3c79a0405 100644 --- a/server/publications/cards.js +++ b/server/publications/cards.js @@ -8,6 +8,27 @@ import Checklists from '../../models/checklists'; import ChecklistItems from '../../models/checklistItems'; import SessionData from '../../models/usersessiondata'; import CustomFields from '../../models/customFields'; +import { + OPERATOR_BOARD, + OPERATOR_DUE, OPERATOR_LIST, OPERATOR_SWIMLANE, OPERATOR_USER, + ORDER_ASCENDING, + PREDICATE_ALL, + PREDICATE_ARCHIVED, + PREDICATE_ASSIGNEES, + PREDICATE_ATTACHMENT, + PREDICATE_CHECKLIST, + PREDICATE_CREATED_AT, + PREDICATE_DESCRIPTION, + PREDICATE_DUE_AT, + PREDICATE_END_AT, + PREDICATE_ENDED, + PREDICATE_MEMBERS, + PREDICATE_MODIFIED_AT, + PREDICATE_PRIVATE, + PREDICATE_PUBLIC, + PREDICATE_START_AT, + PREDICATE_SYSTEM, +} from '../../config/search-const'; const escapeForRegex = require('escape-string-regexp'); @@ -17,12 +38,8 @@ Meteor.publish('card', cardId => { }); Meteor.publish('myCards', function(sessionId) { - const queryParams = { - users: [Meteor.user().username], - // limit: 25, - skip: 0, - // sort: { name: 'dueAt', order: 'des' }, - }; + const queryParams = {} + queryParams[OPERATOR_USER] = [Meteor.user().username]; return findCards(sessionId, buildQuery(queryParams)); }); @@ -61,16 +78,20 @@ Meteor.publish('globalSearch', function(sessionId, queryParams) { class QueryErrors { constructor() { this.notFound = { - boards: [], - swimlanes: [], - lists: [], + // boards: [], + // swimlanes: [], + // lists: [], labels: [], - users: [], + // users: [], members: [], assignees: [], status: [], comments: [], }; + this.notFound[OPERATOR_BOARD] = []; + this.notFound[OPERATOR_LIST] = []; + this.notFound[OPERATOR_SWIMLANE] = []; + this.notFound[OPERATOR_USER] = []; this.colorMap = Boards.colorMap(); } @@ -87,13 +108,13 @@ class QueryErrors { errorMessages() { const messages = []; - this.notFound.boards.forEach(board => { + this.notFound[OPERATOR_BOARD].forEach(board => { messages.push({ tag: 'board-title-not-found', value: board }); }); - this.notFound.swimlanes.forEach(swim => { + this.notFound[OPERATOR_SWIMLANE].forEach(swim => { messages.push({ tag: 'swimlane-title-not-found', value: swim }); }); - this.notFound.lists.forEach(list => { + this.notFound[OPERATOR_LIST].forEach(list => { messages.push({ tag: 'list-title-not-found', value: list }); }); this.notFound.comments.forEach(comments => { @@ -116,7 +137,7 @@ class QueryErrors { }); } }); - this.notFound.users.forEach(user => { + this.notFound[OPERATOR_USER].forEach(user => { messages.push({ tag: 'user-username-not-found', value: user }); }); this.notFound.members.forEach(user => { @@ -163,13 +184,13 @@ function buildSelector(queryParams) { let endAt = null; if (queryParams.status && queryParams.status.length) { queryParams.status.forEach(status => { - if (status === 'archived') { + if (status === PREDICATE_ARCHIVED) { archived = true; - } else if (status === 'all') { + } else if (status === PREDICATE_ALL) { archived = null; - } else if (status === 'ended') { + } else if (status === PREDICATE_ENDED) { endAt = { $nin: [null, ''] }; - } else if (['private', 'public'].includes(status)) { + } else if ([PREDICATE_PRIVATE, PREDICATE_PUBLIC].includes(status)) { boardsSelector.permission = status; } }); @@ -214,9 +235,9 @@ function buildSelector(queryParams) { selector.endAt = endAt; } - if (queryParams.boards && queryParams.boards.length) { + if (queryParams[OPERATOR_BOARD] && queryParams[OPERATOR_BOARD].length) { const queryBoards = []; - queryParams.boards.forEach(query => { + queryParams[OPERATOR_BOARD].forEach(query => { const boards = Boards.userSearch(userId, { title: new RegExp(escapeForRegex(query), 'i'), }); @@ -225,16 +246,19 @@ function buildSelector(queryParams) { queryBoards.push(board._id); }); } else { - errors.notFound.boards.push(query); + errors.notFound[OPERATOR_BOARD].push(query); } }); selector.boardId.$in = queryBoards; } - if (queryParams.swimlanes && queryParams.swimlanes.length) { + if ( + queryParams[OPERATOR_SWIMLANE] && + queryParams[OPERATOR_SWIMLANE].length + ) { const querySwimlanes = []; - queryParams.swimlanes.forEach(query => { + queryParams[OPERATOR_SWIMLANE].forEach(query => { const swimlanes = Swimlanes.find({ title: new RegExp(escapeForRegex(query), 'i'), }); @@ -243,7 +267,7 @@ function buildSelector(queryParams) { querySwimlanes.push(swim._id); }); } else { - errors.notFound.swimlanes.push(query); + errors.notFound[OPERATOR_SWIMLANE].push(query); } }); @@ -254,9 +278,9 @@ function buildSelector(queryParams) { selector.swimlaneId.$in = querySwimlanes; } - if (queryParams.lists && queryParams.lists.length) { + if (queryParams[OPERATOR_LIST] && queryParams[OPERATOR_LIST].length) { const queryLists = []; - queryParams.lists.forEach(query => { + queryParams[OPERATOR_LIST].forEach(query => { const lists = Lists.find({ title: new RegExp(escapeForRegex(query), 'i'), }); @@ -265,7 +289,7 @@ function buildSelector(queryParams) { queryLists.push(list._id); }); } else { - errors.notFound.lists.push(query); + errors.notFound[OPERATOR_LIST].push(query); } }); @@ -289,7 +313,7 @@ function buildSelector(queryParams) { } } - ['dueAt', 'createdAt', 'modifiedAt'].forEach(field => { + [OPERATOR_DUE, 'createdAt', 'modifiedAt'].forEach(field => { if (queryParams[field]) { selector[field] = {}; selector[field][queryParams[field].operator] = new Date( @@ -302,8 +326,8 @@ function buildSelector(queryParams) { members: [], assignees: [], }; - if (queryParams.users && queryParams.users.length) { - queryParams.users.forEach(query => { + if (queryParams[OPERATOR_USER] && queryParams[OPERATOR_USER].length) { + queryParams[OPERATOR_USER].forEach(query => { const users = Users.find({ username: query, }); @@ -313,7 +337,7 @@ function buildSelector(queryParams) { queryUsers.assignees.push(user._id); }); } else { - errors.notFound.users.push(query); + errors.notFound[OPERATOR_USER].push(query); } }); } @@ -405,7 +429,7 @@ function buildSelector(queryParams) { if (queryParams.has && queryParams.has.length) { queryParams.has.forEach(has => { switch (has.field) { - case 'attachment': + case PREDICATE_ATTACHMENT: selector.$and.push({ _id: { $in: Attachments.find({}, { fields: { cardId: 1 } }).map( @@ -414,7 +438,7 @@ function buildSelector(queryParams) { }, }); break; - case 'checklist': + case PREDICATE_CHECKLIST: selector.$and.push({ _id: { $in: Checklists.find({}, { fields: { cardId: 1 } }).map( @@ -423,18 +447,18 @@ function buildSelector(queryParams) { }, }); break; - case 'description': - case 'startAt': - case 'dueAt': - case 'endAt': + case PREDICATE_DESCRIPTION: + case PREDICATE_START_AT: + case PREDICATE_DUE_AT: + case PREDICATE_END_AT: if (has.exists) { selector[has.field] = { $exists: true, $nin: [null, ''] }; } else { selector[has.field] = { $in: [null, ''] }; } break; - case 'assignees': - case 'members': + case PREDICATE_ASSIGNEES: + case PREDICATE_MEMBERS: if (has.exists) { selector[has.field] = { $exists: true, $nin: [null, []] }; } else { @@ -546,9 +570,9 @@ function buildProjection(query) { }; if (query.params.sort) { - const order = query.params.sort.order === 'asc' ? 1 : -1; + const order = query.params.sort.order === ORDER_ASCENDING ? 1 : -1; switch (query.params.sort.name) { - case 'dueAt': + case PREDICATE_DUE_AT: projection.sort = { dueAt: order, boardId: 1, @@ -557,7 +581,7 @@ function buildProjection(query) { sort: 1, }; break; - case 'modifiedAt': + case PREDICATE_MODIFIED_AT: projection.sort = { modifiedAt: order, boardId: 1, @@ -566,7 +590,7 @@ function buildProjection(query) { sort: 1, }; break; - case 'createdAt': + case PREDICATE_CREATED_AT: projection.sort = { createdAt: order, boardId: 1, @@ -575,7 +599,7 @@ function buildProjection(query) { sort: 1, }; break; - case 'system': + case PREDICATE_SYSTEM: projection.sort = { boardId: order, swimlaneId: order, @@ -602,18 +626,15 @@ function buildQuery(queryParams) { } Meteor.publish('brokenCards', function(sessionId) { - const queryParams = { - users: [Meteor.user().username], - // limit: 25, - skip: 0, - // sort: { name: 'dueAt', order: 'des' }, - }; - const query = buildQuery(queryParams); + check(sessionId, String); + + const query = buildQuery({ status: [PREDICATE_ALL] }); query.selector.$or = [ { boardId: { $in: [null, ''] } }, { swimlaneId: { $in: [null, ''] } }, { listId: { $in: [null, ''] } }, ]; + console.log('brokenCards selector:', query.selector); return findCards(sessionId, query); });