Merge branch 'master' of supplee.net:wekan

This commit is contained in:
John R. Supplee 2021-01-09 20:39:45 +02:00
commit 734c56a6b6
10 changed files with 479 additions and 34 deletions

View file

@ -0,0 +1,64 @@
template(name="dueCardsHeaderBar")
h1
//a.back-btn(href="{{pathFor 'home'}}")
// i.fa.fa-chevron-left
| {{_ 'dueCards-title'}}
.board-header-btns.left
a.board-header-btn.js-due-cards-view-change(title="{{_ 'dueCardsViewChange-title'}}")
i.fa.fa-caret-down
if $eq dueCardsView 'me'
i.fa.fa-user
| {{_ 'dueCardsViewChange-choice-me'}}
if $eq dueCardsView 'all'
i.fa.fa-users
| {{_ 'dueCardsViewChange-choice-all'}}
template(name="dueCardsModalTitle")
h2
i.fa.fa-keyboard-o
| {{_ '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
template(name="dueCardsViewChangePopup")
ul.pop-over-list
li
with "dueCardsViewChange-choice-me"
a.js-due-cards-view-me
i.fa.fa-user.colorful
| {{_ 'dueCardsViewChange-choice-me'}}
if $eq Utils.dueCardsView "me"
i.fa.fa-check
li
with "dueCardsViewChange-choice-all"
a.js-due-cards-view-all
i.fa.fa-users.colorful
| {{_ 'dueCardsViewChange-choice-all'}}
if $eq Utils.dueCardsView "all"
i.fa.fa-check

View file

@ -0,0 +1,140 @@
BlazeComponent.extendComponent({
dueCardsView() {
// eslint-disable-next-line no-console
// console.log('sort:', Utils.dueCardsView());
return Utils.dueCardsView();
},
events() {
return [
{
'click .js-due-cards-view-change': Popup.open('dueCardsViewChange'),
},
];
},
}).register('dueCardsHeaderBar');
Template.dueCards.helpers({
userId() {
return Meteor.userId();
},
});
BlazeComponent.extendComponent({
events() {
return [
{
'click .js-due-cards-view-me'() {
Utils.setDueCardsView('me');
Popup.close();
},
'click .js-due-cards-view-all'() {
Utils.setDueCardsView('all');
Popup.close();
},
},
];
},
}).register('dueCardsViewChangePopup');
BlazeComponent.extendComponent({
onCreated() {
Meteor.subscribe('setting');
Meteor.subscribe('dueCards', Utils.dueCardsView() === 'all');
},
dueCardsView() {
// eslint-disable-next-line no-console
console.log('sort:', Utils.dueCardsView());
return Utils.dueCardsView();
},
sortByBoard() {
return this.dueCardsView() === 'board';
},
dueCardsList() {
const allUsers = Utils.dueCardsView() === 'all';
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;
},
}).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

@ -5,15 +5,14 @@ template(name="myCardsHeaderBar")
| {{_ 'my-cards'}}
.board-header-btns.left
a.board-header-btn.js-toggle-my-cards-choose-sort(title="{{_ 'my-cards-sort'}}")
//i.fa.fa-caret-down
i.fa.fa-sort
a.board-header-btn.js-toggle-my-cards-choose-sort(title="{{_ 'myCardsSortChange-title'}}")
i.fa.fa-caret-down
if $eq myCardsSort 'board'
i.fa.fa-th-large
| {{_ 'my-cards-sort-board'}}
| {{_ 'myCardsSortChange-choice-board'}}
if $eq myCardsSort 'dueAt'
i.fa.fa-calendar
| {{_ 'my-cards-sort-dueat'}}
| {{_ 'myCardsSortChange-choice-dueat'}}
template(name="myCardsModalTitle")
h2
@ -50,21 +49,21 @@ template(name="myCards")
ul.my-cards-context-list
li.my-cards-context(title="{{_ 'board'}}")
+viewer
= card.board.title
= card.getBoard.title
li.my-cards-context.my-cards-context-separator
= ' '
| {{_ 'context-separator'}}
= ' '
li.my-cards-context(title="{{_ 'swimlane'}}")
+viewer
= card.swimlane.title
= card.getSwimlane.title
li.my-cards-context
= ' '
| {{_ 'context-separator'}}
= ' '
li.my-cards-context(title="{{_ 'list'}}")
+viewer
= card.list.title
= card.getList.title
template(name="myCardsSortChangePopup")
@ -73,24 +72,13 @@ template(name="myCardsSortChangePopup")
with "my-cards-sort-board"
a.js-my-cards-sort-board
i.fa.fa-th-large.colorful
| {{_ 'my-cards-sort-board'}}
| {{_ 'myCardsSortChange-choice-board'}}
if $eq Utils.myCardsSort "board"
i.fa.fa-check
li
with "my-cards-sort-dueat"
a.js-my-cards-sort-dueat
i.fa.fa-calendar.colorful
| {{_ 'my-cards-sort-dueat'}}
| {{_ 'myCardsSortChange-choice-dueat'}}
if $eq Utils.myCardsSort "dueAt"
i.fa.fa-check
//template(name="myCardsSortChangePopup")
// ul.pop-over-list
// li
// a.js-my-cards-sort-board
// i.fa.fa-th-large.colorful
// | {{_ 'my-cards-sort-board'}}
// li
// a.js-my-cards-sort-dueat
// i.fa.fa-calendar.colorful
// | {{_ 'my-cards-sort-dueat'}}

View file

@ -8,12 +8,9 @@ BlazeComponent.extendComponent({
events() {
return [
{
'click .js-toggle-my-cards-choose-sort'() {
// eslint-disable-next-line no-console
// console.log('open sort');
// Popup.open('myCardsSortChange');
Utils.myCardsSortToggle();
},
'click .js-toggle-my-cards-choose-sort': Popup.open(
'myCardsSortChange',
),
},
];
},
@ -93,7 +90,7 @@ BlazeComponent.extendComponent({
if (list === null || card.listId !== list._id) {
// eslint-disable-next-line no-console
// console.log('new list');
list = card.list();
list = card.getList();
if (list.archived) {
list = null;
return;
@ -104,7 +101,7 @@ BlazeComponent.extendComponent({
if (swimlane === null || card.swimlaneId !== swimlane._id) {
// eslint-disable-next-line no-console
// console.log('new swimlane');
swimlane = card.swimlane();
swimlane = card.getSwimlane();
if (swimlane.archived) {
swimlane = null;
return;
@ -115,7 +112,7 @@ BlazeComponent.extendComponent({
if (board === null || card.boardId !== board._id) {
// eslint-disable-next-line no-console
// console.log('new board');
board = card.board();
board = card.getBoard();
if (board.archived) {
board = null;
return;
@ -200,7 +197,13 @@ BlazeComponent.extendComponent({
const cards = [];
cursor.forEach(card => {
cards.push(card);
if (
!card.getBoard().archived &&
!card.getSwimlane().archived &&
!card.getList().archived
) {
cards.push(card);
}
});
cards.sort((a, b) => {

View file

@ -69,6 +69,31 @@ 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 || !['me', 'all'].includes(view)) {
window.localStorage.setItem('dueCardsView', 'me');
location.reload();
view = 'me';
}
return view;
},
setDueCardsView(view) {
window.localStorage.setItem('dueCardsView', view);
location.reload();
},
// 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: 'due-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

@ -854,7 +854,11 @@
"list": "List",
"board": "Board",
"context-separator": "/",
"my-cards-sort": "My Cards Sort",
"my-cards-sort-board": "By Board",
"my-cards-sort-dueat": "By Due Date"
"myCardsSortChange-title": "My Cards Sort",
"myCardsSortChange-choice-board": "By Board",
"myCardsSortChange-choice-dueat": "By Due Date",
"dueCards-title": "Due Cards",
"dueCardsViewChange-title": "Due Cards View",
"dueCardsViewChange-choice-me": "Me",
"dueCardsViewChange-choice-all": "All Users"
}

View file

@ -469,6 +469,45 @@ Cards.helpers({
return Boards.findOne(this.boardId);
},
getList() {
const list = this.list();
if (!list) {
return {
_id: this.listId,
title: 'Undefined List',
archived: false,
colorClass: '',
};
}
return list;
},
getSwimlane() {
const swimlane = this.swimlane();
if (!swimlane) {
return {
_id: this.swimlaneId,
title: 'Undefined Swimlane',
archived: false,
colorClass: '',
};
}
return swimlane;
},
getBoard() {
const board = this.board();
if (!board) {
return {
_id: this.boardId,
title: 'Undefined Board',
archived: false,
colorClass: '',
};
}
return board;
},
labels() {
const boardLabels = this.board().labels;
const cardLabels = _.filter(boardLabels, label => {

View file

@ -32,3 +32,90 @@ Meteor.publish('myCards', function() {
},
);
});
Meteor.publish('dueCards', function(allUsers = false) {
check(allUsers, Boolean);
// eslint-disable-next-line no-console
// console.log('all users:', allUsers);
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 => {
if (card.boardId) boards.push(card.boardId);
if (card.swimlaneId) swimlanes.push(card.swimlaneId);
if (card.listId) lists.push(card.listId);
});
return [
cards,
Boards.find({ _id: { $in: boards } }),
Swimlanes.find({ _id: { $in: swimlanes } }),
Lists.find({ _id: { $in: lists } }),
];
});