mirror of
https://github.com/wekan/wekan.git
synced 2025-12-16 15:30:13 +01:00
Global search limited working prototype
* added publication * need to filter cards on client side
This commit is contained in:
parent
5913a4af1b
commit
01bd94d2b3
4 changed files with 186 additions and 20 deletions
|
|
@ -9,25 +9,49 @@ template(name="globalSearchModalTitle")
|
||||||
| {{_ 'globalSearch-title'}}
|
| {{_ 'globalSearch-title'}}
|
||||||
|
|
||||||
template(name="globalSearch")
|
template(name="globalSearch")
|
||||||
if isPageReady.get
|
.wrapper
|
||||||
.wrapper
|
form.js-search-query-form
|
||||||
form.js-search-query-form
|
input(type="text" name="searchQuery" placeholder="{{_ 'search-example'}}" autofocus dir="auto")
|
||||||
input(type="text" name="searchQuery" placeholder="{{_ 'search-example'}}" autofocus dir="auto")
|
if searching.get
|
||||||
else
|
|
||||||
+spinner
|
+spinner
|
||||||
|
else if hasResults.get
|
||||||
|
.global-search-dueat-list-wrapper
|
||||||
|
h1 Results
|
||||||
|
each card in results
|
||||||
|
.global-search-card-wrapper
|
||||||
|
a.minicard-wrapper.card-title(href=card.absoluteUrl)
|
||||||
|
+minicard(card)
|
||||||
|
ul.global-search-context-list
|
||||||
|
li.global-search-context(title="{{_ 'board'}}")
|
||||||
|
+viewer
|
||||||
|
= card.getBoard.title
|
||||||
|
li.global-search-context.global-search-context-separator
|
||||||
|
= ' '
|
||||||
|
| {{_ 'context-separator'}}
|
||||||
|
= ' '
|
||||||
|
li.global-search-context(title="{{_ 'swimlane'}}")
|
||||||
|
+viewer
|
||||||
|
= card.getSwimlane.title
|
||||||
|
li.global-search-context
|
||||||
|
= ' '
|
||||||
|
| {{_ 'context-separator'}}
|
||||||
|
= ' '
|
||||||
|
li.global-search-context(title="{{_ 'list'}}")
|
||||||
|
+viewer
|
||||||
|
= card.getList.title
|
||||||
|
|
||||||
template(name="globalSearchViewChangePopup")
|
template(name="globalSearchViewChangePopup")
|
||||||
ul.pop-over-list
|
ul.pop-over-list
|
||||||
li
|
li
|
||||||
with "globalSearchViewChange-choice-me"
|
with "globalSearchViewChange-choice-me"
|
||||||
a.js-due-cards-view-me
|
a.js-global-search-view-me
|
||||||
i.fa.fa-user.colorful
|
i.fa.fa-user.colorful
|
||||||
| {{_ 'globalSearchViewChange-choice-me'}}
|
| {{_ 'globalSearchViewChange-choice-me'}}
|
||||||
if $eq Utils.globalSearchView "me"
|
if $eq Utils.globalSearchView "me"
|
||||||
i.fa.fa-check
|
i.fa.fa-check
|
||||||
li
|
li
|
||||||
with "globalSearchViewChange-choice-all"
|
with "globalSearchViewChange-choice-all"
|
||||||
a.js-due-cards-view-all
|
a.js-global-search-view-all
|
||||||
i.fa.fa-users.colorful
|
i.fa.fa-users.colorful
|
||||||
| {{_ 'globalSearchViewChange-choice-all'}}
|
| {{_ 'globalSearchViewChange-choice-all'}}
|
||||||
span.sub-name
|
span.sub-name
|
||||||
|
|
|
||||||
|
|
@ -37,6 +37,8 @@ BlazeComponent.extendComponent({
|
||||||
BlazeComponent.extendComponent({
|
BlazeComponent.extendComponent({
|
||||||
onCreated() {
|
onCreated() {
|
||||||
this.isPageReady = new ReactiveVar(true);
|
this.isPageReady = new ReactiveVar(true);
|
||||||
|
this.searching = new ReactiveVar(false);
|
||||||
|
this.hasResults = new ReactiveVar(false);
|
||||||
this.query = new ReactiveVar('');
|
this.query = new ReactiveVar('');
|
||||||
|
|
||||||
// this.autorun(() => {
|
// this.autorun(() => {
|
||||||
|
|
@ -50,16 +52,24 @@ BlazeComponent.extendComponent({
|
||||||
Meteor.subscribe('setting');
|
Meteor.subscribe('setting');
|
||||||
},
|
},
|
||||||
|
|
||||||
|
results() {
|
||||||
|
return Cards.find();
|
||||||
|
},
|
||||||
|
|
||||||
events() {
|
events() {
|
||||||
return [
|
return [
|
||||||
{
|
{
|
||||||
'submit .js-search-query-form'(evt) {
|
'submit .js-search-query-form'(evt) {
|
||||||
evt.preventDefault();
|
evt.preventDefault();
|
||||||
this.query.set(evt.target.searchQuery.value);
|
this.query.set(evt.target.searchQuery.value);
|
||||||
// eslint-disable-next-line no-console
|
|
||||||
console.log('query:', this.query.get());
|
this.searching.set(true);
|
||||||
|
this.hasResults.set(false);
|
||||||
|
|
||||||
let query = this.query.get();
|
let query = this.query.get();
|
||||||
|
// eslint-disable-next-line no-console
|
||||||
|
console.log('query:', query);
|
||||||
|
|
||||||
const reUser = /^@(?<user>\w+)(\s+|$)/;
|
const reUser = /^@(?<user>\w+)(\s+|$)/;
|
||||||
const reLabel = /^#(?<label>\w+)(\s+|$)/;
|
const reLabel = /^#(?<label>\w+)(\s+|$)/;
|
||||||
const reOperator1 = /^(?<operator>\w+):(?<value>\w+)(\s+|$)/;
|
const reOperator1 = /^(?<operator>\w+):(?<value>\w+)(\s+|$)/;
|
||||||
|
|
@ -139,6 +149,16 @@ BlazeComponent.extendComponent({
|
||||||
console.log('selector:', selector);
|
console.log('selector:', selector);
|
||||||
// eslint-disable-next-line no-console
|
// eslint-disable-next-line no-console
|
||||||
console.log('text:', text);
|
console.log('text:', text);
|
||||||
|
|
||||||
|
this.autorun(() => {
|
||||||
|
const handle = subManager.subscribe('globalSearch', selector);
|
||||||
|
Tracker.nonreactive(() => {
|
||||||
|
Tracker.autorun(() => {
|
||||||
|
this.searching.set(!handle.ready());
|
||||||
|
this.hasResults.set(handle.ready());
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
.due-cards-board-wrapper
|
.global-search-board-wrapper
|
||||||
border-radius: 8px
|
border-radius: 8px
|
||||||
//padding: 0.5rem
|
//padding: 0.5rem
|
||||||
min-width: 400px
|
min-width: 400px
|
||||||
|
|
@ -9,14 +9,14 @@
|
||||||
margin-right: auto
|
margin-right: auto
|
||||||
margin-left: auto
|
margin-left: auto
|
||||||
|
|
||||||
.due-cards-board-title
|
.global-search-board-title
|
||||||
font-size: 1.4rem
|
font-size: 1.4rem
|
||||||
font-weight: bold
|
font-weight: bold
|
||||||
padding: 0.5rem
|
padding: 0.5rem
|
||||||
background-color: grey
|
background-color: grey
|
||||||
color: white
|
color: white
|
||||||
|
|
||||||
.due-cards-swimlane-title
|
.global-search-swimlane-title
|
||||||
font-size: 1.1rem
|
font-size: 1.1rem
|
||||||
font-weight: bold
|
font-weight: bold
|
||||||
padding: 0.5rem
|
padding: 0.5rem
|
||||||
|
|
@ -30,7 +30,7 @@
|
||||||
.swimlane-default-color
|
.swimlane-default-color
|
||||||
background-color: lightgrey
|
background-color: lightgrey
|
||||||
|
|
||||||
.due-cards-list-title
|
.global-search-list-title
|
||||||
font-weight: bold
|
font-weight: bold
|
||||||
font-size: 1.1rem
|
font-size: 1.1rem
|
||||||
//padding-bottom: 0
|
//padding-bottom: 0
|
||||||
|
|
@ -38,7 +38,7 @@
|
||||||
text-align: center
|
text-align: center
|
||||||
margin-bottom: 0.7rem
|
margin-bottom: 0.7rem
|
||||||
|
|
||||||
.due-cards-list-wrapper
|
.global-search-list-wrapper
|
||||||
margin: 1rem
|
margin: 1rem
|
||||||
border-radius: 5px
|
border-radius: 5px
|
||||||
padding: 1.5rem
|
padding: 1.5rem
|
||||||
|
|
@ -47,23 +47,23 @@
|
||||||
min-width: 250px
|
min-width: 250px
|
||||||
max-width: 350px
|
max-width: 350px
|
||||||
|
|
||||||
.due-cards-card-wrapper
|
.global-search-card-wrapper
|
||||||
margin-top: 0
|
margin-top: 0
|
||||||
margin-bottom: 10px
|
margin-bottom: 10px
|
||||||
|
|
||||||
.due-cards-dueat-list-wrapper
|
.global-search-dueat-list-wrapper
|
||||||
max-width: 500px
|
max-width: 500px
|
||||||
margin-right: auto
|
margin-right: auto
|
||||||
margin-left: auto
|
margin-left: auto
|
||||||
|
|
||||||
.due-cards-field-name
|
.global-search-field-name
|
||||||
font-weight: bold
|
font-weight: bold
|
||||||
|
|
||||||
.due-cards-context
|
.global-search-context
|
||||||
display: inline-block
|
display: inline-block
|
||||||
|
|
||||||
.due-cards-context-separator
|
.global-search-context-separator
|
||||||
font-weight: bold
|
font-weight: bold
|
||||||
|
|
||||||
.due-cards-context-list
|
.global-search-context-list
|
||||||
margin-bottom: 0.7rem
|
margin-bottom: 0.7rem
|
||||||
|
|
|
||||||
|
|
@ -174,3 +174,125 @@ Meteor.publish('dueCards', function(allUsers = false) {
|
||||||
Users.find({ _id: { $in: users } }),
|
Users.find({ _id: { $in: users } }),
|
||||||
];
|
];
|
||||||
});
|
});
|
||||||
|
|
||||||
|
Meteor.publish('globalSearch', function(queryParams) {
|
||||||
|
check(queryParams, Object);
|
||||||
|
|
||||||
|
// eslint-disable-next-line no-console
|
||||||
|
console.log('selector:', queryParams);
|
||||||
|
|
||||||
|
const user = Users.findOne(this.userId);
|
||||||
|
|
||||||
|
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);
|
||||||
|
});
|
||||||
|
|
||||||
|
const permiitedBoards = [];
|
||||||
|
let selector = {
|
||||||
|
archived: false,
|
||||||
|
};
|
||||||
|
// if user is not an admin allow her to see cards only from boards where
|
||||||
|
// she is a member
|
||||||
|
if (!user.isAdmin) {
|
||||||
|
selector.$or = [
|
||||||
|
{ permission: 'public' },
|
||||||
|
{ members: { $elemMatch: { userId: user._id, isActive: true } } },
|
||||||
|
];
|
||||||
|
}
|
||||||
|
if (queryParams.boards.length) {
|
||||||
|
selector.title = { $in: [] };
|
||||||
|
queryParams.boards.forEach(term => {
|
||||||
|
selector.title.$in.push(term);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
Boards.find(selector).forEach(board => {
|
||||||
|
permiitedBoards.push(board._id);
|
||||||
|
});
|
||||||
|
|
||||||
|
const searchLists = [];
|
||||||
|
if (queryParams.lists.length) {
|
||||||
|
selector = {
|
||||||
|
archived: false,
|
||||||
|
title: { $in: [] },
|
||||||
|
};
|
||||||
|
queryParams.lists.forEach(term => {
|
||||||
|
selector.title.$in.push(term);
|
||||||
|
});
|
||||||
|
|
||||||
|
// eslint-disable-next-line no-console
|
||||||
|
console.log('search list selector:', selector);
|
||||||
|
Lists.find(selector).forEach(list => {
|
||||||
|
searchLists.push(list._id);
|
||||||
|
});
|
||||||
|
// eslint-disable-next-line no-console
|
||||||
|
console.log('search lists:', searchLists);
|
||||||
|
}
|
||||||
|
|
||||||
|
selector = {
|
||||||
|
archived: false,
|
||||||
|
boardId: { $nin: archivedBoards, $in: permiitedBoards },
|
||||||
|
swimlaneId: { $nin: archivedSwimlanes },
|
||||||
|
listId: { $nin: archivedLists },
|
||||||
|
};
|
||||||
|
|
||||||
|
if (searchLists.length) {
|
||||||
|
selector.listId.$in = searchLists;
|
||||||
|
}
|
||||||
|
|
||||||
|
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 } }),
|
||||||
|
];
|
||||||
|
});
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue