mirror of
https://github.com/wekan/wekan.git
synced 2025-09-22 01:50:48 +02:00
Initial work on due cards page
This commit is contained in:
parent
108d01ee35
commit
1abdd5177d
7 changed files with 483 additions and 1 deletions
76
client/components/main/dueCards.jade
Normal file
76
client/components/main/dueCards.jade
Normal file
|
@ -0,0 +1,76 @@
|
|||
template(name="dueCardsHeaderBar")
|
||||
h1
|
||||
//a.back-btn(href="{{pathFor 'home'}}")
|
||||
// i.fa.fa-chevron-left
|
||||
| {{_ 'due-cards'}}
|
||||
|
||||
.board-header-btns.left
|
||||
a.board-header-btn.js-toggle-due-cards-choose-sort(title="{{_ 'due-cards-sort'}}")
|
||||
//i.fa.fa-caret-down
|
||||
i.fa.fa-sort
|
||||
if $eq dueCardsView 'user'
|
||||
i.fa.fa-th-large
|
||||
| {{_ 'due-cards-sort-board'}}
|
||||
if $eq dueCardsView 'all'
|
||||
i.fa.fa-calendar
|
||||
| {{_ 'due-cards-sort-dueat'}}
|
||||
|
||||
template(name="dueCardsModalTitle")
|
||||
h2
|
||||
i.fa.fa-keyboard-o
|
||||
| {{_ 'due-cards'}}
|
||||
|
||||
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.board.title
|
||||
li.due-cards-context.due-cards-context-separator
|
||||
= ' '
|
||||
| {{_ 'context-separator'}}
|
||||
= ' '
|
||||
li.due-cards-context(title="{{_ 'swimlane'}}")
|
||||
+viewer
|
||||
= card.swimlane.title
|
||||
li.due-cards-context
|
||||
= ' '
|
||||
| {{_ 'context-separator'}}
|
||||
= ' '
|
||||
li.due-cards-context(title="{{_ 'list'}}")
|
||||
+viewer
|
||||
= card.list.title
|
||||
|
||||
|
||||
template(name="dueCardsViewChangePopup")
|
||||
ul.pop-over-list
|
||||
li
|
||||
with "due-cards-view-user"
|
||||
a.js-due-cards-sort-board
|
||||
i.fa.fa-th-large.colorful
|
||||
| {{_ 'due-cards-view-user'}}
|
||||
if $eq Utils.dueCardsView "user"
|
||||
i.fa.fa-check
|
||||
li
|
||||
with "due-cards-view-all"
|
||||
a.js-due-cards-sort-dueat
|
||||
i.fa.fa-calendar.colorful
|
||||
| {{_ 'due-cards-view-all'}}
|
||||
if $eq Utils.dueCardsView "all"
|
||||
i.fa.fa-check
|
||||
|
||||
//template(name="dueCardsViewChangePopup")
|
||||
// ul.pop-over-list
|
||||
// li
|
||||
// a.js-due-cards-sort-board
|
||||
// i.fa.fa-th-large.colorful
|
||||
// | {{_ 'due-cards-sort-board'}}
|
||||
// li
|
||||
// a.js-due-cards-sort-dueat
|
||||
// i.fa.fa-calendar.colorful
|
||||
// | {{_ 'due-cards-sort-dueat'}}
|
163
client/components/main/dueCards.js
Normal file
163
client/components/main/dueCards.js
Normal file
|
@ -0,0 +1,163 @@
|
|||
BlazeComponent.extendComponent({
|
||||
dueCardsView() {
|
||||
// eslint-disable-next-line no-console
|
||||
// console.log('sort:', Utils.dueCardsView());
|
||||
return Utils.dueCardsView();
|
||||
},
|
||||
|
||||
events() {
|
||||
return [
|
||||
{
|
||||
'click .js-toggle-my-cards-choose-sort'() {
|
||||
// eslint-disable-next-line no-console
|
||||
// console.log('open sort');
|
||||
// Popup.open('dueCardsViewChange');
|
||||
Utils.dueCardsViewToggle();
|
||||
},
|
||||
},
|
||||
];
|
||||
},
|
||||
}).register('dueCardsHeaderBar');
|
||||
|
||||
Template.dueCards.helpers({
|
||||
userId() {
|
||||
return Meteor.userId();
|
||||
},
|
||||
});
|
||||
|
||||
BlazeComponent.extendComponent({
|
||||
events() {
|
||||
return [
|
||||
{
|
||||
'click .js-my-cards-sort-board'() {
|
||||
Utils.setMyCardsSort('board');
|
||||
Popup.close();
|
||||
},
|
||||
|
||||
'click .js-my-cards-sort-dueat'() {
|
||||
Utils.setMyCardsSort('dueAt');
|
||||
Popup.close();
|
||||
},
|
||||
},
|
||||
];
|
||||
},
|
||||
}).register('dueCardsViewChangePopup');
|
||||
|
||||
BlazeComponent.extendComponent({
|
||||
onCreated() {
|
||||
Meteor.subscribe('setting');
|
||||
Meteor.subscribe('dueCards');
|
||||
},
|
||||
|
||||
dueCardsView() {
|
||||
// eslint-disable-next-line no-console
|
||||
console.log('sort:', Utils.dueCardsView());
|
||||
return Utils.dueCardsView();
|
||||
},
|
||||
|
||||
sortByBoard() {
|
||||
return this.dueCardsView() === 'board';
|
||||
},
|
||||
|
||||
dueCardsList() {
|
||||
const allUsers = false;
|
||||
|
||||
const user = Meteor.user();
|
||||
|
||||
const archivedBoards = [];
|
||||
Boards.find({ archived: true }).forEach(board => {
|
||||
archivedBoards.push(board._id);
|
||||
});
|
||||
|
||||
const permiitedBoards = [];
|
||||
let selector = {
|
||||
archived: false,
|
||||
};
|
||||
// if user is not an 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 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(),
|
||||
// );
|
||||
});
|
||||
|
||||
cards.sort((a, b) => {
|
||||
const x = a.dueAt === null ? Date('2100-12-31') : a.dueAt;
|
||||
const y = b.dueAt === null ? Date('2100-12-31') : b.dueAt;
|
||||
|
||||
if (x > y) return 1;
|
||||
else if (x < y) return -1;
|
||||
|
||||
return 0;
|
||||
});
|
||||
|
||||
// eslint-disable-next-line no-console
|
||||
// console.log('cards:', 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('dueCards');
|
69
client/components/main/dueCards.styl
Normal file
69
client/components/main/dueCards.styl
Normal file
|
@ -0,0 +1,69 @@
|
|||
.due-cards-board-wrapper
|
||||
border-radius: 8px
|
||||
//padding: 0.5rem
|
||||
min-width: 400px
|
||||
border-width: 8px
|
||||
border-color: grey
|
||||
border-style: solid
|
||||
margin-bottom: 2rem
|
||||
margin-right: auto
|
||||
margin-left: auto
|
||||
|
||||
.due-cards-board-title
|
||||
font-size: 1.4rem
|
||||
font-weight: bold
|
||||
padding: 0.5rem
|
||||
background-color: grey
|
||||
color: white
|
||||
|
||||
.due-cards-swimlane-title
|
||||
font-size: 1.1rem
|
||||
font-weight: bold
|
||||
padding: 0.5rem
|
||||
padding-bottom: 0.4rem
|
||||
margin-top: 0
|
||||
margin-bottom: 0.5rem
|
||||
//border-top: black 1px solid
|
||||
//border-bottom: black 1px solid
|
||||
text-align: center
|
||||
|
||||
.swimlane-default-color
|
||||
background-color: lightgrey
|
||||
|
||||
.due-cards-list-title
|
||||
font-weight: bold
|
||||
font-size: 1.1rem
|
||||
//padding-bottom: 0
|
||||
//margin-bottom: 0
|
||||
text-align: center
|
||||
margin-bottom: 0.7rem
|
||||
|
||||
.due-cards-list-wrapper
|
||||
margin: 1rem
|
||||
border-radius: 5px
|
||||
padding: 1.5rem
|
||||
padding-top: 0.75rem
|
||||
display: inline-block
|
||||
min-width: 250px
|
||||
max-width: 350px
|
||||
|
||||
.due-cards-card-wrapper
|
||||
margin-top: 0
|
||||
margin-bottom: 10px
|
||||
|
||||
.due-cards-dueat-list-wrapper
|
||||
max-width: 500px
|
||||
margin-right: auto
|
||||
margin-left: auto
|
||||
|
||||
.due-cards-field-name
|
||||
font-weight: bold
|
||||
|
||||
.due-cards-context
|
||||
display: inline-block
|
||||
|
||||
.due-cards-context-separator
|
||||
font-weight: bold
|
||||
|
||||
.due-cards-context-list
|
||||
margin-bottom: 0.7rem
|
|
@ -200,7 +200,13 @@ BlazeComponent.extendComponent({
|
|||
|
||||
const cards = [];
|
||||
cursor.forEach(card => {
|
||||
cards.push(card);
|
||||
if (
|
||||
!card.board().archived &&
|
||||
!card.swimlane().archived &&
|
||||
!card.list().archived
|
||||
) {
|
||||
cards.push(card);
|
||||
}
|
||||
});
|
||||
|
||||
cards.sort((a, b) => {
|
||||
|
|
|
@ -69,6 +69,64 @@ Utils = {
|
|||
location.reload();
|
||||
},
|
||||
|
||||
archivedBoardIds() {
|
||||
const archivedBoards = [];
|
||||
Boards.find({ archived: false }).forEach(board => {
|
||||
archivedBoards.push(board._id);
|
||||
});
|
||||
return archivedBoards;
|
||||
},
|
||||
|
||||
dueCardsView() {
|
||||
let view = window.localStorage.getItem('dueCardsView');
|
||||
|
||||
if (!view || !['user', 'all'].includes(view)) {
|
||||
window.localStorage.setItem('dueCardsView', 'user');
|
||||
location.reload();
|
||||
view = 'user';
|
||||
}
|
||||
|
||||
return view;
|
||||
},
|
||||
|
||||
dueBoardsSelector() {
|
||||
const user = Meteor.user();
|
||||
|
||||
const 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 } } },
|
||||
];
|
||||
}
|
||||
|
||||
return selector;
|
||||
},
|
||||
|
||||
dueCardsSelector() {
|
||||
const user = Meteor.user();
|
||||
|
||||
const 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 } } },
|
||||
];
|
||||
}
|
||||
|
||||
return selector;
|
||||
},
|
||||
|
||||
// XXX We should remove these two methods
|
||||
goBoardId(_id) {
|
||||
const board = Boards.findOne(_id);
|
||||
|
|
|
@ -139,6 +139,32 @@ FlowRouter.route('/my-cards', {
|
|||
},
|
||||
});
|
||||
|
||||
FlowRouter.route('/due-cards', {
|
||||
name: 'my-cards',
|
||||
action() {
|
||||
const dueCardsTemplate = 'dueCards';
|
||||
|
||||
Filter.reset();
|
||||
// EscapeActions.executeAll();
|
||||
EscapeActions.executeUpTo('popup-close');
|
||||
|
||||
Utils.manageCustomUI();
|
||||
Utils.manageMatomo();
|
||||
|
||||
// if (previousPath) {
|
||||
// Modal.open(dueCardsTemplate, {
|
||||
// header: 'dueCardsModalTitle',
|
||||
// onCloseGoTo: previousPath,
|
||||
// });
|
||||
// } else {
|
||||
BlazeLayout.render('defaultLayout', {
|
||||
headerBar: 'dueCardsHeaderBar',
|
||||
content: dueCardsTemplate,
|
||||
});
|
||||
// }
|
||||
},
|
||||
});
|
||||
|
||||
FlowRouter.route('/import/:source', {
|
||||
name: 'import',
|
||||
triggersEnter: [AccountsTemplates.ensureSignedIn],
|
||||
|
|
|
@ -32,3 +32,87 @@ Meteor.publish('myCards', function() {
|
|||
},
|
||||
);
|
||||
});
|
||||
|
||||
Meteor.publish('dueCards', function(allUsers = false) {
|
||||
check(allUsers, Boolean);
|
||||
|
||||
const user = Users.findOne(this.userId);
|
||||
|
||||
const archivedBoards = [];
|
||||
Boards.find({ archived: true }).forEach(board => {
|
||||
archivedBoards.push(board._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 } } },
|
||||
];
|
||||
}
|
||||
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 = [];
|
||||
|
||||
cards.forEach(card => {
|
||||
boards.push(card.boardId);
|
||||
swimlanes.push(card.swimlaneId);
|
||||
lists.push(card.listId);
|
||||
});
|
||||
|
||||
return [
|
||||
cards,
|
||||
Boards.find({ _id: { $in: boards } }),
|
||||
Swimlanes.find({ _id: { $in: swimlanes } }),
|
||||
Lists.find({ _id: { $in: lists } }),
|
||||
];
|
||||
});
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue