Initial work on due cards page

This commit is contained in:
John R. Supplee 2021-01-07 22:36:10 +02:00
parent 108d01ee35
commit 1abdd5177d
7 changed files with 483 additions and 1 deletions

View 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'}}

View 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');

View 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

View file

@ -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) => {

View file

@ -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);

View file

@ -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],

View file

@ -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 } }),
];
});