mirror of
https://github.com/wekan/wekan.git
synced 2025-04-23 13:37:09 -04:00
My Cards and Due Cards development
* add spinner while pages are loading * use a single publication for My Cards * add Due Cards to the user menu * add description to the All Users option for Due Cards * some code clean-up
This commit is contained in:
parent
55b121e0d3
commit
ecc3558987
10 changed files with 197 additions and 185 deletions
|
@ -1,7 +1,6 @@
|
|||
template(name="dueCardsHeaderBar")
|
||||
h1
|
||||
//a.back-btn(href="{{pathFor 'home'}}")
|
||||
// i.fa.fa-chevron-left
|
||||
i.fa.fa-calendar
|
||||
| {{_ 'dueCards-title'}}
|
||||
|
||||
.board-header-btns.left
|
||||
|
@ -20,31 +19,33 @@ template(name="dueCardsModalTitle")
|
|||
| {{_ 'dueCards-title'}}
|
||||
|
||||
template(name="dueCards")
|
||||
.wrapper
|
||||
.due-cards-dueat-list-wrapper
|
||||
each card in dueCardsList
|
||||
.due-cards-card-wrapper
|
||||
a.minicard-wrapper.card-title(href=card.absoluteUrl)
|
||||
+minicard(card)
|
||||
ul.due-cards-context-list
|
||||
li.due-cards-context(title="{{_ 'board'}}")
|
||||
+viewer
|
||||
= card.getBoard.title
|
||||
li.due-cards-context.due-cards-context-separator
|
||||
= ' '
|
||||
| {{_ 'context-separator'}}
|
||||
= ' '
|
||||
li.due-cards-context(title="{{_ 'swimlane'}}")
|
||||
+viewer
|
||||
= card.getSwimlane.title
|
||||
li.due-cards-context
|
||||
= ' '
|
||||
| {{_ 'context-separator'}}
|
||||
= ' '
|
||||
li.due-cards-context(title="{{_ 'list'}}")
|
||||
+viewer
|
||||
= card.getList.title
|
||||
|
||||
if isPageReady.get
|
||||
.wrapper
|
||||
.due-cards-dueat-list-wrapper
|
||||
each card in dueCardsList
|
||||
.due-cards-card-wrapper
|
||||
a.minicard-wrapper.card-title(href=card.absoluteUrl)
|
||||
+minicard(card)
|
||||
ul.due-cards-context-list
|
||||
li.due-cards-context(title="{{_ 'board'}}")
|
||||
+viewer
|
||||
= card.getBoard.title
|
||||
li.due-cards-context.due-cards-context-separator
|
||||
= ' '
|
||||
| {{_ 'context-separator'}}
|
||||
= ' '
|
||||
li.due-cards-context(title="{{_ 'swimlane'}}")
|
||||
+viewer
|
||||
= card.getSwimlane.title
|
||||
li.due-cards-context
|
||||
= ' '
|
||||
| {{_ 'context-separator'}}
|
||||
= ' '
|
||||
li.due-cards-context(title="{{_ 'list'}}")
|
||||
+viewer
|
||||
= card.getList.title
|
||||
else
|
||||
+spinner
|
||||
|
||||
template(name="dueCardsViewChangePopup")
|
||||
ul.pop-over-list
|
||||
|
@ -60,5 +61,8 @@ template(name="dueCardsViewChangePopup")
|
|||
a.js-due-cards-view-all
|
||||
i.fa.fa-users.colorful
|
||||
| {{_ 'dueCardsViewChange-choice-all'}}
|
||||
span.sub-name
|
||||
+viewer
|
||||
| {{_ 'dueCardsViewChange-choice-all-description' }}
|
||||
if $eq Utils.dueCardsView "all"
|
||||
i.fa.fa-check
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
const subManager = new SubsManager();
|
||||
|
||||
BlazeComponent.extendComponent({
|
||||
dueCardsView() {
|
||||
// eslint-disable-next-line no-console
|
||||
|
@ -40,8 +42,20 @@ BlazeComponent.extendComponent({
|
|||
|
||||
BlazeComponent.extendComponent({
|
||||
onCreated() {
|
||||
this.isPageReady = new ReactiveVar(false);
|
||||
|
||||
this.autorun(() => {
|
||||
const handle = subManager.subscribe(
|
||||
'dueCards',
|
||||
Utils.dueCardsView() === 'all',
|
||||
);
|
||||
Tracker.nonreactive(() => {
|
||||
Tracker.autorun(() => {
|
||||
this.isPageReady.set(handle.ready());
|
||||
});
|
||||
});
|
||||
});
|
||||
Meteor.subscribe('setting');
|
||||
Meteor.subscribe('dueCards', Utils.dueCardsView() === 'all');
|
||||
},
|
||||
|
||||
dueCardsView() {
|
||||
|
|
|
@ -2,6 +2,7 @@ template(name="myCardsHeaderBar")
|
|||
h1
|
||||
//a.back-btn(href="{{pathFor 'home'}}")
|
||||
// i.fa.fa-chevron-left
|
||||
i.fa.fa-list
|
||||
| {{_ 'my-cards'}}
|
||||
|
||||
.board-header-btns.left
|
||||
|
@ -20,51 +21,53 @@ template(name="myCardsModalTitle")
|
|||
| {{_ 'my-cards'}}
|
||||
|
||||
template(name="myCards")
|
||||
.wrapper
|
||||
if $eq myCardsSort 'board'
|
||||
each board in myBoards
|
||||
.my-cards-board-wrapper
|
||||
.my-cards-board-title
|
||||
+viewer
|
||||
= board.title
|
||||
each swimlane in board.mySwimlanes
|
||||
.my-cards-swimlane-title(class="{{#if swimlane.colorClass}}{{ swimlane.colorClass }}{{else}}swimlane-default-color{{/if}}")
|
||||
if isPageReady.get
|
||||
.wrapper
|
||||
if $eq myCardsSort 'board'
|
||||
each board in myCardsList
|
||||
.my-cards-board-wrapper
|
||||
.my-cards-board-title
|
||||
+viewer
|
||||
= swimlane.title
|
||||
each list in swimlane.myLists
|
||||
.my-cards-list-wrapper
|
||||
.my-cards-list-title(class=list.colorClass)
|
||||
= board.title
|
||||
each swimlane in board.mySwimlanes
|
||||
.my-cards-swimlane-title(class="{{#if swimlane.colorClass}}{{ swimlane.colorClass }}{{else}}swimlane-default-color{{/if}}")
|
||||
+viewer
|
||||
= swimlane.title
|
||||
each list in swimlane.myLists
|
||||
.my-cards-list-wrapper
|
||||
.my-cards-list-title(class=list.colorClass)
|
||||
+viewer
|
||||
= list.title
|
||||
each card in list.myCards
|
||||
.my-cards-card-wrapper
|
||||
a.minicard-wrapper(href=card.absoluteUrl)
|
||||
+minicard(card)
|
||||
else
|
||||
.my-cards-dueat-list-wrapper
|
||||
each card in myDueCardsList
|
||||
.my-cards-card-wrapper
|
||||
a.minicard-wrapper.card-title(href=card.absoluteUrl)
|
||||
+minicard(card)
|
||||
ul.my-cards-context-list
|
||||
li.my-cards-context(title="{{_ 'board'}}")
|
||||
+viewer
|
||||
= list.title
|
||||
each card in list.myCards
|
||||
.my-cards-card-wrapper
|
||||
a.minicard-wrapper(href=card.absoluteUrl)
|
||||
+minicard(card)
|
||||
else
|
||||
.my-cards-dueat-list-wrapper
|
||||
each card in myCardsList
|
||||
.my-cards-card-wrapper
|
||||
a.minicard-wrapper.card-title(href=card.absoluteUrl)
|
||||
+minicard(card)
|
||||
ul.my-cards-context-list
|
||||
li.my-cards-context(title="{{_ 'board'}}")
|
||||
+viewer
|
||||
= card.getBoard.title
|
||||
li.my-cards-context.my-cards-context-separator
|
||||
= ' '
|
||||
| {{_ 'context-separator'}}
|
||||
= ' '
|
||||
li.my-cards-context(title="{{_ 'swimlane'}}")
|
||||
+viewer
|
||||
= card.getSwimlane.title
|
||||
li.my-cards-context
|
||||
= ' '
|
||||
| {{_ 'context-separator'}}
|
||||
= ' '
|
||||
li.my-cards-context(title="{{_ 'list'}}")
|
||||
+viewer
|
||||
= card.getList.title
|
||||
|
||||
= card.getBoard.title
|
||||
li.my-cards-context.my-cards-context-separator
|
||||
= ' '
|
||||
| {{_ 'context-separator'}}
|
||||
= ' '
|
||||
li.my-cards-context(title="{{_ 'swimlane'}}")
|
||||
+viewer
|
||||
= card.getSwimlane.title
|
||||
li.my-cards-context
|
||||
= ' '
|
||||
| {{_ 'context-separator'}}
|
||||
= ' '
|
||||
li.my-cards-context(title="{{_ 'list'}}")
|
||||
+viewer
|
||||
= card.getList.title
|
||||
else
|
||||
+spinner
|
||||
|
||||
template(name="myCardsSortChangePopup")
|
||||
ul.pop-over-list
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
const subManager = new SubsManager();
|
||||
|
||||
BlazeComponent.extendComponent({
|
||||
myCardsSort() {
|
||||
// eslint-disable-next-line no-console
|
||||
|
@ -42,10 +44,17 @@ BlazeComponent.extendComponent({
|
|||
|
||||
BlazeComponent.extendComponent({
|
||||
onCreated() {
|
||||
this.isPageReady = new ReactiveVar(false);
|
||||
|
||||
this.autorun(() => {
|
||||
const handle = subManager.subscribe('myCards');
|
||||
Tracker.nonreactive(() => {
|
||||
Tracker.autorun(() => {
|
||||
this.isPageReady.set(handle.ready());
|
||||
});
|
||||
});
|
||||
});
|
||||
Meteor.subscribe('setting');
|
||||
Meteor.subscribe('myCards');
|
||||
Meteor.subscribe('mySwimlanes');
|
||||
Meteor.subscribe('myLists');
|
||||
},
|
||||
|
||||
myCardsSort() {
|
||||
|
@ -58,7 +67,7 @@ BlazeComponent.extendComponent({
|
|||
return this.myCardsSort() === 'board';
|
||||
},
|
||||
|
||||
myBoards() {
|
||||
myCardsList() {
|
||||
const userId = Meteor.userId();
|
||||
const boards = [];
|
||||
let board = null;
|
||||
|
@ -173,7 +182,7 @@ BlazeComponent.extendComponent({
|
|||
return boards;
|
||||
},
|
||||
|
||||
myCardsList() {
|
||||
myDueCardsList() {
|
||||
const userId = Meteor.userId();
|
||||
|
||||
const cursor = Cards.find(
|
||||
|
|
|
@ -17,6 +17,10 @@ template(name="memberMenuPopup")
|
|||
a.js-my-cards(href="{{pathFor 'my-cards'}}")
|
||||
i.fa.fa-list
|
||||
| {{_ 'my-cards'}}
|
||||
li
|
||||
a.js-due-cards(href="{{pathFor 'due-cards'}}")
|
||||
i.fa.fa-calendar
|
||||
| {{_ 'dueCards-title'}}
|
||||
li
|
||||
a(href="{{pathFor 'home'}}")
|
||||
span.fa.fa-home
|
||||
|
|
|
@ -28,6 +28,9 @@ Template.memberMenuPopup.events({
|
|||
'click .js-my-cards'() {
|
||||
Popup.close();
|
||||
},
|
||||
'click .js-due-cards'() {
|
||||
Popup.close();
|
||||
},
|
||||
'click .js-open-archived-board'() {
|
||||
Modal.open('archivedBoards');
|
||||
},
|
||||
|
|
|
@ -116,8 +116,6 @@ FlowRouter.route('/shortcuts', {
|
|||
FlowRouter.route('/my-cards', {
|
||||
name: 'my-cards',
|
||||
action() {
|
||||
const myCardsTemplate = 'myCards';
|
||||
|
||||
Filter.reset();
|
||||
// EscapeActions.executeAll();
|
||||
EscapeActions.executeUpTo('popup-close');
|
||||
|
@ -125,15 +123,9 @@ FlowRouter.route('/my-cards', {
|
|||
Utils.manageCustomUI();
|
||||
Utils.manageMatomo();
|
||||
|
||||
// if (previousPath) {
|
||||
// Modal.open(myCardsTemplate, {
|
||||
// header: 'myCardsModalTitle',
|
||||
// onCloseGoTo: previousPath,
|
||||
// });
|
||||
// } else {
|
||||
BlazeLayout.render('defaultLayout', {
|
||||
headerBar: 'myCardsHeaderBar',
|
||||
content: myCardsTemplate,
|
||||
content: 'myCards',
|
||||
});
|
||||
// }
|
||||
},
|
||||
|
@ -142,8 +134,6 @@ FlowRouter.route('/my-cards', {
|
|||
FlowRouter.route('/due-cards', {
|
||||
name: 'due-cards',
|
||||
action() {
|
||||
const dueCardsTemplate = 'dueCards';
|
||||
|
||||
Filter.reset();
|
||||
// EscapeActions.executeAll();
|
||||
EscapeActions.executeUpTo('popup-close');
|
||||
|
@ -151,15 +141,9 @@ FlowRouter.route('/due-cards', {
|
|||
Utils.manageCustomUI();
|
||||
Utils.manageMatomo();
|
||||
|
||||
// if (previousPath) {
|
||||
// Modal.open(dueCardsTemplate, {
|
||||
// header: 'dueCardsModalTitle',
|
||||
// onCloseGoTo: previousPath,
|
||||
// });
|
||||
// } else {
|
||||
BlazeLayout.render('defaultLayout', {
|
||||
headerBar: 'dueCardsHeaderBar',
|
||||
content: dueCardsTemplate,
|
||||
content: 'dueCards',
|
||||
});
|
||||
// }
|
||||
},
|
||||
|
|
|
@ -860,5 +860,6 @@
|
|||
"dueCards-title": "Due Cards",
|
||||
"dueCardsViewChange-title": "Due Cards View",
|
||||
"dueCardsViewChange-choice-me": "Me",
|
||||
"dueCardsViewChange-choice-all": "All Users"
|
||||
"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."
|
||||
}
|
||||
|
|
|
@ -43,71 +43,6 @@ Meteor.publish('boards', function() {
|
|||
);
|
||||
});
|
||||
|
||||
Meteor.publish('mySwimlanes', function() {
|
||||
const userId = this.userId;
|
||||
const swimlanes = [];
|
||||
|
||||
Cards.find({
|
||||
archived: false,
|
||||
$or: [{ members: userId }, { assignees: userId }],
|
||||
}).forEach(card => {
|
||||
swimlanes.push(card.swimlaneId);
|
||||
});
|
||||
|
||||
return Swimlanes.find(
|
||||
{
|
||||
// archived: false,
|
||||
_id: { $in: swimlanes },
|
||||
},
|
||||
{
|
||||
fields: {
|
||||
_id: 1,
|
||||
title: 1,
|
||||
boardId: 1,
|
||||
type: 1,
|
||||
color: 1,
|
||||
sort: 1,
|
||||
},
|
||||
// sort: {
|
||||
// sort: ['boardId', 'listId', 'sort'],
|
||||
// },
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
Meteor.publish('myLists', function() {
|
||||
const userId = this.userId;
|
||||
const lists = [];
|
||||
|
||||
Cards.find({
|
||||
archived: false,
|
||||
$or: [{ members: userId }, { assignees: userId }],
|
||||
}).forEach(card => {
|
||||
lists.push(card.listId);
|
||||
});
|
||||
|
||||
return Lists.find(
|
||||
{
|
||||
// archived: false,
|
||||
_id: { $in: lists },
|
||||
},
|
||||
{
|
||||
fields: {
|
||||
_id: 1,
|
||||
boardId: 1,
|
||||
swimlaneId: 1,
|
||||
title: 1,
|
||||
color: 1,
|
||||
type: 1,
|
||||
sort: 1,
|
||||
},
|
||||
// sort: {
|
||||
// sort: ['boardId', 'listId', 'sort'],
|
||||
// },
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
Meteor.publish('archivedBoards', function() {
|
||||
const userId = this.userId;
|
||||
if (!Match.test(userId, String)) return [];
|
||||
|
|
|
@ -4,33 +4,76 @@ Meteor.publish('card', cardId => {
|
|||
});
|
||||
|
||||
Meteor.publish('myCards', function() {
|
||||
const userId = this.userId;
|
||||
const userId = Meteor.userId();
|
||||
|
||||
return Cards.find(
|
||||
{
|
||||
archived: false,
|
||||
$or: [{ members: userId }, { assignees: 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);
|
||||
});
|
||||
|
||||
selector = {
|
||||
archived: false,
|
||||
boardId: { $nin: archivedBoards },
|
||||
swimlaneId: { $nin: archivedSwimlanes },
|
||||
listId: { $nin: archivedLists },
|
||||
$or: [{ members: userId }, { assignees: userId }],
|
||||
};
|
||||
|
||||
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,
|
||||
},
|
||||
{
|
||||
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,
|
||||
},
|
||||
// sort: {
|
||||
// sort: ['boardId', 'listId', 'sort'],
|
||||
// },
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
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 } }),
|
||||
];
|
||||
});
|
||||
|
||||
Meteor.publish('dueCards', function(allUsers = false) {
|
||||
|
@ -105,11 +148,22 @@ Meteor.publish('dueCards', function(allUsers = false) {
|
|||
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 [
|
||||
|
@ -117,5 +171,6 @@ Meteor.publish('dueCards', function(allUsers = false) {
|
|||
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