Migrate sidebar and settings components from BlazeComponent to Template

Convert sidebar, sidebarArchives, sidebarCustomFields, sidebarFilters,
sidebarSearches, and all settings panel components to use native Meteor
Template.onCreated/helpers/events pattern.
This commit is contained in:
Harry Adel 2026-03-08 11:01:21 +02:00
parent d3625db755
commit bae23f9ed8
12 changed files with 2937 additions and 3046 deletions

View file

@ -1,143 +1,178 @@
import { ReactiveCache } from '/imports/reactiveCache'; import { ReactiveCache } from '/imports/reactiveCache';
import { TAPi18n } from '/imports/i18n'; import { TAPi18n } from '/imports/i18n';
import { AttachmentStorage } from '/models/attachments'; import { AttachmentStorage } from '/models/attachments';
import { CardSearchPagedComponent } from '/client/lib/cardSearch'; import { CardSearchPaged } from '/client/lib/cardSearch';
import SessionData from '/models/usersessiondata'; import SessionData from '/models/usersessiondata';
import { QueryParams } from '/config/query-classes'; import { QueryParams } from '/config/query-classes';
import { OPERATOR_LIMIT } from '/config/search-const'; import { OPERATOR_LIMIT } from '/config/search-const';
const filesize = require('filesize'); const filesize = require('filesize');
BlazeComponent.extendComponent({ // --- Shared helper functions (formerly AdminReport base class methods) ---
subscription: null,
showFilesReport: new ReactiveVar(false),
showBrokenCardsReport: new ReactiveVar(false),
showOrphanedFilesReport: new ReactiveVar(false),
showRulesReport: new ReactiveVar(false),
showCardsReport: new ReactiveVar(false),
showBoardsReport: new ReactiveVar(false),
sessionId: null,
onCreated() { function yesOrNo(value) {
this.error = new ReactiveVar(''); if (value) {
this.loading = new ReactiveVar(false); return TAPi18n.__('yes');
this.sessionId = SessionData.getSessionId(); } else {
}, return TAPi18n.__('no');
events() {
return [
{
'click a.js-report-broken': this.switchMenu,
'click a.js-report-files': this.switchMenu,
'click a.js-report-rules': this.switchMenu,
'click a.js-report-cards': this.switchMenu,
'click a.js-report-boards': this.switchMenu,
},
];
},
switchMenu(event) {
const target = $(event.target);
if (!target.hasClass('active')) {
this.loading.set(true);
this.showFilesReport.set(false);
this.showBrokenCardsReport.set(false);
this.showOrphanedFilesReport.set(false);
this.showRulesReport.set(false)
this.showBoardsReport.set(false);
this.showCardsReport.set(false);
if (this.subscription) {
this.subscription.stop();
}
$('.side-menu li.active').removeClass('active');
target.parent().addClass('active');
const targetID = target.data('id');
if ('report-broken' === targetID) {
this.showBrokenCardsReport.set(true);
this.subscription = Meteor.subscribe(
'brokenCards',
SessionData.getSessionId(),
() => {
this.loading.set(false);
},
);
} else if ('report-files' === targetID) {
this.showFilesReport.set(true);
this.subscription = Meteor.subscribe('attachmentsList', () => {
this.loading.set(false);
});
} else if ('report-rules' === targetID) {
this.subscription = Meteor.subscribe('rulesReport', () => {
this.showRulesReport.set(true);
this.loading.set(false);
});
} else if ('report-boards' === targetID) {
this.subscription = Meteor.subscribe('boardsReport', () => {
this.showBoardsReport.set(true);
this.loading.set(false);
});
} else if ('report-cards' === targetID) {
const qp = new QueryParams();
qp.addPredicate(OPERATOR_LIMIT, 300);
this.subscription = Meteor.subscribe(
'globalSearch',
this.sessionId,
qp.getParams(),
qp.text,
() => {
this.showCardsReport.set(true);
this.loading.set(false);
},
);
}
}
},
}).register('adminReports');
class AdminReport extends BlazeComponent {
collection;
results() {
// eslint-disable-next-line no-console
return this.collection.find();
}
yesOrNo(value) {
if (value) {
return TAPi18n.__('yes');
} else {
return TAPi18n.__('no');
}
}
resultsCount() {
return this.collection.find().count();
}
fileSize(size) {
let ret = "";
if (_.isNumber(size)) {
ret = filesize(size);
}
return ret;
}
abbreviate(text) {
if (text?.length > 30) {
return `${text.substr(0, 29)}...`;
}
return text;
} }
} }
(class extends AdminReport { function fileSizeHelper(size) {
collection = Attachments; let ret = "";
}.register('filesReport')); if (_.isNumber(size)) {
ret = filesize(size);
}
return ret;
}
(class extends AdminReport { function abbreviate(text) {
collection = Rules; if (text?.length > 30) {
return `${text.substr(0, 29)}...`;
}
return text;
}
function collectionResults(collection) {
return collection.find();
}
function collectionResultsCount(collection) {
return collection.find().count();
}
// --- adminReports template ---
Template.adminReports.onCreated(function () {
this.subscription = null;
this.showFilesReport = new ReactiveVar(false);
this.showBrokenCardsReport = new ReactiveVar(false);
this.showOrphanedFilesReport = new ReactiveVar(false);
this.showRulesReport = new ReactiveVar(false);
this.showCardsReport = new ReactiveVar(false);
this.showBoardsReport = new ReactiveVar(false);
this.sessionId = SessionData.getSessionId();
this.error = new ReactiveVar('');
this.loading = new ReactiveVar(false);
});
Template.adminReports.helpers({
showFilesReport() {
return Template.instance().showFilesReport;
},
showBrokenCardsReport() {
return Template.instance().showBrokenCardsReport;
},
showOrphanedFilesReport() {
return Template.instance().showOrphanedFilesReport;
},
showRulesReport() {
return Template.instance().showRulesReport;
},
showCardsReport() {
return Template.instance().showCardsReport;
},
showBoardsReport() {
return Template.instance().showBoardsReport;
},
loading() {
return Template.instance().loading;
},
});
Template.adminReports.events({
'click a.js-report-broken'(event) {
switchMenu(event, Template.instance());
},
'click a.js-report-files'(event) {
switchMenu(event, Template.instance());
},
'click a.js-report-rules'(event) {
switchMenu(event, Template.instance());
},
'click a.js-report-cards'(event) {
switchMenu(event, Template.instance());
},
'click a.js-report-boards'(event) {
switchMenu(event, Template.instance());
},
});
function switchMenu(event, tmpl) {
const target = $(event.target);
if (!target.hasClass('active')) {
tmpl.loading.set(true);
tmpl.showFilesReport.set(false);
tmpl.showBrokenCardsReport.set(false);
tmpl.showOrphanedFilesReport.set(false);
tmpl.showRulesReport.set(false);
tmpl.showBoardsReport.set(false);
tmpl.showCardsReport.set(false);
if (tmpl.subscription) {
tmpl.subscription.stop();
}
$('.side-menu li.active').removeClass('active');
target.parent().addClass('active');
const targetID = target.data('id');
if ('report-broken' === targetID) {
tmpl.showBrokenCardsReport.set(true);
tmpl.subscription = Meteor.subscribe(
'brokenCards',
SessionData.getSessionId(),
() => {
tmpl.loading.set(false);
},
);
} else if ('report-files' === targetID) {
tmpl.showFilesReport.set(true);
tmpl.subscription = Meteor.subscribe('attachmentsList', () => {
tmpl.loading.set(false);
});
} else if ('report-rules' === targetID) {
tmpl.subscription = Meteor.subscribe('rulesReport', () => {
tmpl.showRulesReport.set(true);
tmpl.loading.set(false);
});
} else if ('report-boards' === targetID) {
tmpl.subscription = Meteor.subscribe('boardsReport', () => {
tmpl.showBoardsReport.set(true);
tmpl.loading.set(false);
});
} else if ('report-cards' === targetID) {
const qp = new QueryParams();
qp.addPredicate(OPERATOR_LIMIT, 300);
tmpl.subscription = Meteor.subscribe(
'globalSearch',
tmpl.sessionId,
qp.getParams(),
qp.text,
() => {
tmpl.showCardsReport.set(true);
tmpl.loading.set(false);
},
);
}
}
}
// --- filesReport template ---
Template.filesReport.helpers({
results() {
return collectionResults(Attachments);
},
resultsCount() {
return collectionResultsCount(Attachments);
},
fileSize(size) {
return fileSizeHelper(size);
},
});
// --- rulesReport template ---
Template.rulesReport.helpers({
results() { results() {
const rules = []; const rules = [];
@ -155,12 +190,27 @@ class AdminReport extends BlazeComponent {
// eslint-disable-next-line no-console // eslint-disable-next-line no-console
console.log('rows:', rules); console.log('rows:', rules);
return rules; return rules;
} },
}.register('rulesReport')); resultsCount() {
return collectionResultsCount(Rules);
},
});
(class extends AdminReport { // --- boardsReport template ---
collection = Boards;
Template.boardsReport.helpers({
results() {
return collectionResults(Boards);
},
resultsCount() {
return collectionResultsCount(Boards);
},
yesOrNo(value) {
return yesOrNo(value);
},
abbreviate(text) {
return abbreviate(text);
},
userNames(members) { userNames(members) {
const ret = (members || []) const ret = (members || [])
.map(_member => { .map(_member => {
@ -169,7 +219,7 @@ class AdminReport extends BlazeComponent {
}) })
.join(", "); .join(", ");
return ret; return ret;
} },
teams(memberTeams) { teams(memberTeams) {
const ret = (memberTeams || []) const ret = (memberTeams || [])
.map(_memberTeam => { .map(_memberTeam => {
@ -178,7 +228,7 @@ class AdminReport extends BlazeComponent {
}) })
.join(", "); .join(", ");
return ret; return ret;
} },
orgs(orgs) { orgs(orgs) {
const ret = (orgs || []) const ret = (orgs || [])
.map(_orgs => { .map(_orgs => {
@ -187,13 +237,21 @@ class AdminReport extends BlazeComponent {
}) })
.join(", "); .join(", ");
return ret; return ret;
} },
}.register('boardsReport')); });
// --- cardsReport template ---
(class extends AdminReport { Template.cardsReport.helpers({
collection = Cards; results() {
return collectionResults(Cards);
},
resultsCount() {
return collectionResultsCount(Cards);
},
abbreviate(text) {
return abbreviate(text);
},
userNames(userIds) { userNames(userIds) {
const ret = (userIds || []) const ret = (userIds || [])
.map(_userId => { .map(_userId => {
@ -201,13 +259,45 @@ class AdminReport extends BlazeComponent {
return _ret; return _ret;
}) })
.join(", "); .join(", ");
return ret return ret;
} },
}.register('cardsReport')); });
class BrokenCardsComponent extends CardSearchPagedComponent { // --- brokenCardsReport template ---
onCreated() {
super.onCreated(); Template.brokenCardsReport.onCreated(function () {
} const search = new CardSearchPaged(this);
} this.search = search;
BrokenCardsComponent.register('brokenCardsReport'); });
Template.brokenCardsReport.helpers({
resultsCount() {
return Template.instance().search.resultsCount;
},
resultsHeading() {
return Template.instance().search.resultsHeading;
},
results() {
return Template.instance().search.results;
},
getSearchHref() {
return Template.instance().search.getSearchHref();
},
hasPreviousPage() {
return Template.instance().search.hasPreviousPage;
},
hasNextPage() {
return Template.instance().search.hasNextPage;
},
});
Template.brokenCardsReport.events({
'click .js-next-page'(evt, tpl) {
evt.preventDefault();
tpl.search.nextPage();
},
'click .js-previous-page'(evt, tpl) {
evt.preventDefault();
tpl.search.previousPage();
},
});

View file

@ -2,31 +2,31 @@ import { ReactiveCache } from '/imports/reactiveCache';
import Attachments, { fileStoreStrategyFactory } from '/models/attachments'; import Attachments, { fileStoreStrategyFactory } from '/models/attachments';
const filesize = require('filesize'); const filesize = require('filesize');
BlazeComponent.extendComponent({ Template.attachments.onCreated(function () {
subscription: null, this.subscription = null;
showMoveAttachments: new ReactiveVar(false), this.showMoveAttachments = new ReactiveVar(false);
sessionId: null, this.sessionId = null;
this.error = new ReactiveVar('');
this.loading = new ReactiveVar(false);
});
onCreated() { Template.attachments.helpers({
this.error = new ReactiveVar(''); loading() {
this.loading = new ReactiveVar(false); return Template.instance().loading;
}, },
showMoveAttachments() {
events() { return Template.instance().showMoveAttachments;
return [
{
'click a.js-move-attachments': this.switchMenu,
},
];
}, },
});
switchMenu(event) { Template.attachments.events({
'click a.js-move-attachments'(event, tpl) {
const target = $(event.target); const target = $(event.target);
if (!target.hasClass('active')) { if (!target.hasClass('active')) {
this.loading.set(true); tpl.loading.set(true);
this.showMoveAttachments.set(false); tpl.showMoveAttachments.set(false);
if (this.subscription) { if (tpl.subscription) {
this.subscription.stop(); tpl.subscription.stop();
} }
$('.side-menu li.active').removeClass('active'); $('.side-menu li.active').removeClass('active');
@ -34,25 +34,30 @@ BlazeComponent.extendComponent({
const targetID = target.data('id'); const targetID = target.data('id');
if ('move-attachments' === targetID) { if ('move-attachments' === targetID) {
this.showMoveAttachments.set(true); tpl.showMoveAttachments.set(true);
this.subscription = Meteor.subscribe('attachmentsList', () => { tpl.subscription = Meteor.subscribe('attachmentsList', () => {
this.loading.set(false); tpl.loading.set(false);
}); });
} }
} }
}, },
}).register('attachments'); });
BlazeComponent.extendComponent({ Template.moveAttachments.onCreated(function () {
this.attachments = null;
});
Template.moveAttachments.helpers({
getBoardsWithAttachments() { getBoardsWithAttachments() {
this.attachments = ReactiveCache.getAttachments(); const tpl = Template.instance();
this.attachmentsByBoardId = _.chain(this.attachments) tpl.attachments = ReactiveCache.getAttachments();
const attachmentsByBoardId = _.chain(tpl.attachments)
.groupBy(fileObj => fileObj.meta.boardId) .groupBy(fileObj => fileObj.meta.boardId)
.value(); .value();
const ret = Object.keys(this.attachmentsByBoardId) const ret = Object.keys(attachmentsByBoardId)
.map(boardId => { .map(boardId => {
const boardAttachments = this.attachmentsByBoardId[boardId]; const boardAttachments = attachmentsByBoardId[boardId];
_.each(boardAttachments, _attachment => { _.each(boardAttachments, _attachment => {
_attachment.flatVersion = Object.keys(_attachment.versions) _attachment.flatVersion = Object.keys(_attachment.versions)
@ -72,71 +77,65 @@ BlazeComponent.extendComponent({
const ret = ReactiveCache.getBoard(boardId); const ret = ReactiveCache.getBoard(boardId);
return ret; return ret;
}, },
events() { });
return [
{
'click button.js-move-all-attachments-to-fs'(event) {
this.attachments.forEach(_attachment => {
Meteor.call('moveAttachmentToStorage', _attachment._id, "fs");
});
},
'click button.js-move-all-attachments-to-gridfs'(event) {
this.attachments.forEach(_attachment => {
Meteor.call('moveAttachmentToStorage', _attachment._id, "gridfs");
});
},
'click button.js-move-all-attachments-to-s3'(event) {
this.attachments.forEach(_attachment => {
Meteor.call('moveAttachmentToStorage', _attachment._id, "s3");
});
},
}
]
}
}).register('moveAttachments');
BlazeComponent.extendComponent({ Template.moveAttachments.events({
events() { 'click button.js-move-all-attachments-to-fs'(event, tpl) {
return [ tpl.attachments.forEach(_attachment => {
{ Meteor.call('moveAttachmentToStorage', _attachment._id, "fs");
'click button.js-move-all-attachments-of-board-to-fs'(event) { });
this.data().attachments.forEach(_attachment => {
Meteor.call('moveAttachmentToStorage', _attachment._id, "fs");
});
},
'click button.js-move-all-attachments-of-board-to-gridfs'(event) {
this.data().attachments.forEach(_attachment => {
Meteor.call('moveAttachmentToStorage', _attachment._id, "gridfs");
});
},
'click button.js-move-all-attachments-of-board-to-s3'(event) {
this.data().attachments.forEach(_attachment => {
Meteor.call('moveAttachmentToStorage', _attachment._id, "s3");
});
},
}
]
}, },
}).register('moveBoardAttachments'); 'click button.js-move-all-attachments-to-gridfs'(event, tpl) {
tpl.attachments.forEach(_attachment => {
Meteor.call('moveAttachmentToStorage', _attachment._id, "gridfs");
});
},
'click button.js-move-all-attachments-to-s3'(event, tpl) {
tpl.attachments.forEach(_attachment => {
Meteor.call('moveAttachmentToStorage', _attachment._id, "s3");
});
},
});
BlazeComponent.extendComponent({ Template.moveBoardAttachments.events({
'click button.js-move-all-attachments-of-board-to-fs'() {
const data = Template.currentData();
data.attachments.forEach(_attachment => {
Meteor.call('moveAttachmentToStorage', _attachment._id, "fs");
});
},
'click button.js-move-all-attachments-of-board-to-gridfs'() {
const data = Template.currentData();
data.attachments.forEach(_attachment => {
Meteor.call('moveAttachmentToStorage', _attachment._id, "gridfs");
});
},
'click button.js-move-all-attachments-of-board-to-s3'() {
const data = Template.currentData();
data.attachments.forEach(_attachment => {
Meteor.call('moveAttachmentToStorage', _attachment._id, "s3");
});
},
});
Template.moveAttachment.helpers({
fileSize(size) { fileSize(size) {
const ret = filesize(size); const ret = filesize(size);
return ret; return ret;
}, },
events() { });
return [
{ Template.moveAttachment.events({
'click button.js-move-storage-fs'(event) { 'click button.js-move-storage-fs'() {
Meteor.call('moveAttachmentToStorage', this.data()._id, "fs"); const data = Template.currentData();
}, Meteor.call('moveAttachmentToStorage', data._id, "fs");
'click button.js-move-storage-gridfs'(event) {
Meteor.call('moveAttachmentToStorage', this.data()._id, "gridfs");
},
'click button.js-move-storage-s3'(event) {
Meteor.call('moveAttachmentToStorage', this.data()._id, "s3");
},
}
]
}, },
}).register('moveAttachment'); 'click button.js-move-storage-gridfs'() {
const data = Template.currentData();
Meteor.call('moveAttachmentToStorage', data._id, "gridfs");
},
'click button.js-move-storage-s3'() {
const data = Template.currentData();
Meteor.call('moveAttachmentToStorage', data._id, "s3");
},
});

View file

@ -1,18 +1,18 @@
import { TAPi18n } from '/imports/i18n'; import { TAPi18n } from '/imports/i18n';
const filesize = require('filesize'); const filesize = require('filesize');
BlazeComponent.extendComponent({ Template.statistics.onCreated(function () {
onCreated() { this.info = new ReactiveVar({});
this.info = new ReactiveVar({}); Meteor.call('getStatistics', (error, ret) => {
Meteor.call('getStatistics', (error, ret) => { if (!error && ret) {
if (!error && ret) { this.info.set(ret);
this.info.set(ret); }
} });
}); });
},
Template.statistics.helpers({
statistics() { statistics() {
return this.info.get(); return Template.instance().info.get();
}, },
humanReadableTime(time) { humanReadableTime(time) {
@ -47,4 +47,4 @@ BlazeComponent.extendComponent({
} }
return ret; return ret;
}, },
}).register('statistics'); });

View file

@ -1,16 +1,12 @@
import { ReactiveCache } from '/imports/reactiveCache'; import { ReactiveCache } from '/imports/reactiveCache';
import LockoutSettings from '/models/lockoutSettings'; import LockoutSettings from '/models/lockoutSettings';
BlazeComponent.extendComponent({ Template.lockedUsersGeneral.onCreated(function () {
onCreated() { this.lockedUsers = new ReactiveVar([]);
this.lockedUsers = new ReactiveVar([]); this.isLoadingLockedUsers = new ReactiveVar(false);
this.isLoadingLockedUsers = new ReactiveVar(false);
// Don't load immediately to prevent unnecessary spinner // Store refreshLockedUsers on the instance so it can be called from event handlers
// The data will be loaded when the tab is selected in peopleBody.js switchMenu this.refreshLockedUsers = () => {
},
refreshLockedUsers() {
// Set loading state initially, but we'll hide it if no users are found // Set loading state initially, but we'll hide it if no users are found
this.isLoadingLockedUsers.set(true); this.isLoadingLockedUsers.set(true);
@ -44,9 +40,54 @@ BlazeComponent.extendComponent({
this.lockedUsers.set(users); this.lockedUsers.set(users);
this.isLoadingLockedUsers.set(false); this.isLoadingLockedUsers.set(false);
}); });
};
// Don't load immediately to prevent unnecessary spinner
// The data will be loaded when the tab is selected in peopleBody.js switchMenu
});
Template.lockedUsersGeneral.helpers({
knownFailuresBeforeLockout() {
return LockoutSettings.findOne('known-failuresBeforeLockout')?.value || 3;
}, },
unlockUser(event) { knownLockoutPeriod() {
return LockoutSettings.findOne('known-lockoutPeriod')?.value || 60;
},
knownFailureWindow() {
return LockoutSettings.findOne('known-failureWindow')?.value || 15;
},
unknownFailuresBeforeLockout() {
return LockoutSettings.findOne('unknown-failuresBeforeLockout')?.value || 3;
},
unknownLockoutPeriod() {
return LockoutSettings.findOne('unknown-lockoutPeriod')?.value || 60;
},
unknownFailureWindow() {
return LockoutSettings.findOne('unknown-failureWindow')?.value || 15;
},
lockedUsers() {
return Template.instance().lockedUsers.get();
},
isLoadingLockedUsers() {
return Template.instance().isLoadingLockedUsers.get();
},
});
Template.lockedUsersGeneral.events({
'click button.js-refresh-locked-users'(event, tpl) {
tpl.refreshLockedUsers();
},
'click button#refreshLockedUsers'(event, tpl) {
tpl.refreshLockedUsers();
},
'click button.js-unlock-user'(event, tpl) {
const userId = $(event.currentTarget).data('user-id'); const userId = $(event.currentTarget).data('user-id');
if (!userId) return; if (!userId) return;
@ -61,13 +102,12 @@ BlazeComponent.extendComponent({
if (result) { if (result) {
alert(TAPi18n.__('accounts-lockout-user-unlocked')); alert(TAPi18n.__('accounts-lockout-user-unlocked'));
this.refreshLockedUsers(); tpl.refreshLockedUsers();
} }
}); });
} }
}, },
'click button.js-unlock-all-users'(event, tpl) {
unlockAllUsers() {
if (confirm(TAPi18n.__('accounts-lockout-confirm-unlock-all'))) { if (confirm(TAPi18n.__('accounts-lockout-confirm-unlock-all'))) {
Meteor.call('unlockAllUsers', (err, result) => { Meteor.call('unlockAllUsers', (err, result) => {
if (err) { if (err) {
@ -79,13 +119,12 @@ BlazeComponent.extendComponent({
if (result) { if (result) {
alert(TAPi18n.__('accounts-lockout-user-unlocked')); alert(TAPi18n.__('accounts-lockout-user-unlocked'));
this.refreshLockedUsers(); tpl.refreshLockedUsers();
} }
}); });
} }
}, },
'click button.js-lockout-save'() {
saveLockoutSettings() {
// Get values from form // Get values from form
const knownFailuresBeforeLockout = parseInt($('#known-failures-before-lockout').val(), 10) || 3; const knownFailuresBeforeLockout = parseInt($('#known-failures-before-lockout').val(), 10) || 3;
const knownLockoutPeriod = parseInt($('#known-lockout-period').val(), 10) || 60; const knownLockoutPeriod = parseInt($('#known-lockout-period').val(), 10) || 60;
@ -128,48 +167,4 @@ BlazeComponent.extendComponent({
} }
}); });
}, },
});
knownFailuresBeforeLockout() {
return LockoutSettings.findOne('known-failuresBeforeLockout')?.value || 3;
},
knownLockoutPeriod() {
return LockoutSettings.findOne('known-lockoutPeriod')?.value || 60;
},
knownFailureWindow() {
return LockoutSettings.findOne('known-failureWindow')?.value || 15;
},
unknownFailuresBeforeLockout() {
return LockoutSettings.findOne('unknown-failuresBeforeLockout')?.value || 3;
},
unknownLockoutPeriod() {
return LockoutSettings.findOne('unknown-lockoutPeriod')?.value || 60;
},
unknownFailureWindow() {
return LockoutSettings.findOne('unknown-failureWindow')?.value || 15;
},
lockedUsers() {
return this.lockedUsers.get();
},
isLoadingLockedUsers() {
return this.isLoadingLockedUsers.get();
},
events() {
return [
{
'click button.js-refresh-locked-users': this.refreshLockedUsers,
'click button#refreshLockedUsers': this.refreshLockedUsers,
'click button.js-unlock-user': this.unlockUser,
'click button.js-unlock-all-users': this.unlockAllUsers,
'click button.js-lockout-save': this.saveLockoutSettings,
},
];
},
}).register('lockedUsersGeneral');

View file

@ -1,4 +1,5 @@
import { ReactiveCache } from '/imports/reactiveCache'; import { ReactiveCache } from '/imports/reactiveCache';
import { InfiniteScrolling } from '/client/lib/infiniteScrolling';
import LockoutSettings from '/models/lockoutSettings'; import LockoutSettings from '/models/lockoutSettings';
import { FlowRouter } from 'meteor/ostrio:flow-router-extra'; import { FlowRouter } from 'meteor/ostrio:flow-router-extra';
@ -8,139 +9,73 @@ const usersPerPage = 25;
let userOrgsTeamsAction = ""; //poosible actions 'addOrg', 'addTeam', 'removeOrg' or 'removeTeam' when adding or modifying a user let userOrgsTeamsAction = ""; //poosible actions 'addOrg', 'addTeam', 'removeOrg' or 'removeTeam' when adding or modifying a user
let selectedUserChkBoxUserIds = []; let selectedUserChkBoxUserIds = [];
BlazeComponent.extendComponent({ Template.people.onCreated(function () {
mixins() { this.infiniteScrolling = new InfiniteScrolling();
return [Mixins.InfiniteScrolling];
},
onCreated() {
this.error = new ReactiveVar('');
this.loading = new ReactiveVar(false);
this.orgSetting = new ReactiveVar(true);
this.teamSetting = new ReactiveVar(false);
this.peopleSetting = new ReactiveVar(false);
this.lockedUsersSetting = new ReactiveVar(false);
this.findOrgsOptions = new ReactiveVar({});
this.findTeamsOptions = new ReactiveVar({});
this.findUsersOptions = new ReactiveVar({});
this.numberOrgs = new ReactiveVar(0);
this.numberTeams = new ReactiveVar(0);
this.numberPeople = new ReactiveVar(0);
this.userFilterType = new ReactiveVar('all');
this.page = new ReactiveVar(1); this.error = new ReactiveVar('');
this.loadNextPageLocked = false; this.loading = new ReactiveVar(false);
this.callFirstWith(null, 'resetNextPeak'); this.orgSetting = new ReactiveVar(true);
this.autorun(() => { this.teamSetting = new ReactiveVar(false);
const limitOrgs = this.page.get() * orgsPerPage; this.peopleSetting = new ReactiveVar(false);
const limitTeams = this.page.get() * teamsPerPage; this.lockedUsersSetting = new ReactiveVar(false);
const limitUsers = this.page.get() * usersPerPage; this.findOrgsOptions = new ReactiveVar({});
this.findTeamsOptions = new ReactiveVar({});
this.findUsersOptions = new ReactiveVar({});
this.numberOrgs = new ReactiveVar(0);
this.numberTeams = new ReactiveVar(0);
this.numberPeople = new ReactiveVar(0);
this.userFilterType = new ReactiveVar('all');
this.subscribe('org', this.findOrgsOptions.get(), limitOrgs, () => { this.page = new ReactiveVar(1);
this.loadNextPageLocked = false; this.loadNextPageLocked = false;
const nextPeakBefore = this.callFirstWith(null, 'getNextPeak'); this.infiniteScrolling.resetNextPeak();
this.calculateNextPeak();
const nextPeakAfter = this.callFirstWith(null, 'getNextPeak'); this.calculateNextPeak = () => {
if (nextPeakBefore === nextPeakAfter) { const element = this.find('.main-body');
this.callFirstWith(null, 'resetNextPeak'); if (element) {
} const altitude = element.scrollHeight;
this.infiniteScrolling.setNextPeak(altitude);
}
};
this.loadNextPage = () => {
if (this.loadNextPageLocked === false) {
this.page.set(this.page.get() + 1);
this.loadNextPageLocked = true;
}
};
this.filterOrg = () => {
const value = $('#searchOrgInput').first().val();
if (value !== '') {
const regex = new RegExp(value, 'i');
this.findOrgsOptions.set({
$or: [
{ orgDisplayName: regex },
{ orgShortName: regex },
],
}); });
} else {
this.findOrgsOptions.set({});
}
};
this.subscribe('team', this.findTeamsOptions.get(), limitTeams, () => { this.filterTeam = () => {
this.loadNextPageLocked = false; const value = $('#searchTeamInput').first().val();
const nextPeakBefore = this.callFirstWith(null, 'getNextPeak'); if (value !== '') {
this.calculateNextPeak(); const regex = new RegExp(value, 'i');
const nextPeakAfter = this.callFirstWith(null, 'getNextPeak'); this.findTeamsOptions.set({
if (nextPeakBefore === nextPeakAfter) { $or: [
this.callFirstWith(null, 'resetNextPeak'); { teamDisplayName: regex },
} { teamShortName: regex },
],
}); });
} else {
this.findTeamsOptions.set({});
}
};
this.subscribe('people', this.findUsersOptions.get(), limitUsers, () => { this.filterPeople = () => {
this.loadNextPageLocked = false;
const nextPeakBefore = this.callFirstWith(null, 'getNextPeak');
this.calculateNextPeak();
const nextPeakAfter = this.callFirstWith(null, 'getNextPeak');
if (nextPeakBefore === nextPeakAfter) {
this.callFirstWith(null, 'resetNextPeak');
}
});
});
},
events() {
return [
{
'click #searchOrgButton'() {
this.filterOrg();
},
'keydown #searchOrgInput'(event) {
if (event.keyCode === 13 && !event.shiftKey) {
this.filterOrg();
}
},
'click #searchTeamButton'() {
this.filterTeam();
},
'keydown #searchTeamInput'(event) {
if (event.keyCode === 13 && !event.shiftKey) {
this.filterTeam();
}
},
'click #searchButton'() {
this.filterPeople();
},
'click #addOrRemoveTeam'(){
document.getElementById("divAddOrRemoveTeamContainer").style.display = 'block';
},
'keydown #searchInput'(event) {
if (event.keyCode === 13 && !event.shiftKey) {
this.filterPeople();
}
},
'change #userFilterSelect'(event) {
const filterType = $(event.target).val();
this.userFilterType.set(filterType);
this.filterPeople();
},
'click #unlockAllUsers'(event) {
event.preventDefault();
if (confirm(TAPi18n.__('accounts-lockout-confirm-unlock-all'))) {
Meteor.call('unlockAllUsers', (error) => {
if (error) {
console.error('Error unlocking all users:', error);
} else {
// Show a brief success message
const message = document.createElement('div');
message.className = 'unlock-all-success';
message.textContent = TAPi18n.__('accounts-lockout-all-users-unlocked');
document.body.appendChild(message);
// Remove the message after a short delay
setTimeout(() => {
if (message.parentNode) {
message.parentNode.removeChild(message);
}
}, 3000);
}
});
}
},
'click #newOrgButton'() {
Popup.open('newOrg');
},
'click #newTeamButton'() {
Popup.open('newTeam');
},
'click #newUserButton'() {
Popup.open('newUser');
},
'click a.js-org-menu': this.switchMenu,
'click a.js-team-menu': this.switchMenu,
'click a.js-people-menu': this.switchMenu,
'click a.js-locked-users-menu': this.switchMenu,
},
];
},
filterPeople() {
const value = $('#searchInput').first().val(); const value = $('#searchInput').first().val();
const filterType = this.userFilterType.get(); const filterType = this.userFilterType.get();
const currentTime = Number(new Date()); const currentTime = Number(new Date());
@ -184,72 +119,9 @@ BlazeComponent.extendComponent({
} }
this.findUsersOptions.set(query); this.findUsersOptions.set(query);
}, };
loadNextPage() {
if (this.loadNextPageLocked === false) { this.switchMenu = (event) => {
this.page.set(this.page.get() + 1);
this.loadNextPageLocked = true;
}
},
calculateNextPeak() {
const element = this.find('.main-body');
if (element) {
const altitude = element.scrollHeight;
this.callFirstWith(this, 'setNextPeak', altitude);
}
},
reachNextPeak() {
this.loadNextPage();
},
setError(error) {
this.error.set(error);
},
setLoading(w) {
this.loading.set(w);
},
orgList() {
const limitOrgs = this.page.get() * orgsPerPage;
const orgs = ReactiveCache.getOrgs(this.findOrgsOptions.get(), {
sort: { orgDisplayName: 1 },
limit: limitOrgs,
fields: { _id: true },
});
// Count only the items currently loaded to browser, not total from database
this.numberOrgs.set(orgs.length);
return orgs;
},
teamList() {
const limitTeams = this.page.get() * teamsPerPage;
const teams = ReactiveCache.getTeams(this.findTeamsOptions.get(), {
sort: { teamDisplayName: 1 },
limit: limitTeams,
fields: { _id: true },
});
// Count only the items currently loaded to browser, not total from database
this.numberTeams.set(teams.length);
return teams;
},
peopleList() {
const limitUsers = this.page.get() * usersPerPage;
const users = ReactiveCache.getUsers(this.findUsersOptions.get(), {
sort: { username: 1 },
limit: limitUsers,
fields: { _id: true },
});
// Count only the items currently loaded to browser, not total from database
this.numberPeople.set(users.length);
return users;
},
orgNumber() {
return this.numberOrgs.get();
},
teamNumber() {
return this.numberTeams.get();
},
peopleNumber() {
return this.numberPeople.get();
},
switchMenu(event) {
const target = $(event.target); const target = $(event.target);
if (!target.hasClass('active')) { if (!target.hasClass('active')) {
$('.side-menu li.active').removeClass('active'); $('.side-menu li.active').removeClass('active');
@ -269,8 +141,191 @@ BlazeComponent.extendComponent({
} }
} }
} }
};
this.autorun(() => {
const limitOrgs = this.page.get() * orgsPerPage;
const limitTeams = this.page.get() * teamsPerPage;
const limitUsers = this.page.get() * usersPerPage;
this.subscribe('org', this.findOrgsOptions.get(), limitOrgs, () => {
this.loadNextPageLocked = false;
const nextPeakBefore = this.infiniteScrolling.getNextPeak();
this.calculateNextPeak();
const nextPeakAfter = this.infiniteScrolling.getNextPeak();
if (nextPeakBefore === nextPeakAfter) {
this.infiniteScrolling.resetNextPeak();
}
});
this.subscribe('team', this.findTeamsOptions.get(), limitTeams, () => {
this.loadNextPageLocked = false;
const nextPeakBefore = this.infiniteScrolling.getNextPeak();
this.calculateNextPeak();
const nextPeakAfter = this.infiniteScrolling.getNextPeak();
if (nextPeakBefore === nextPeakAfter) {
this.infiniteScrolling.resetNextPeak();
}
});
this.subscribe('people', this.findUsersOptions.get(), limitUsers, () => {
this.loadNextPageLocked = false;
const nextPeakBefore = this.infiniteScrolling.getNextPeak();
this.calculateNextPeak();
const nextPeakAfter = this.infiniteScrolling.getNextPeak();
if (nextPeakBefore === nextPeakAfter) {
this.infiniteScrolling.resetNextPeak();
}
});
});
});
Template.people.helpers({
loading() {
return Template.instance().loading;
}, },
}).register('people'); orgSetting() {
return Template.instance().orgSetting;
},
teamSetting() {
return Template.instance().teamSetting;
},
peopleSetting() {
return Template.instance().peopleSetting;
},
lockedUsersSetting() {
return Template.instance().lockedUsersSetting;
},
orgList() {
const tpl = Template.instance();
const limitOrgs = tpl.page.get() * orgsPerPage;
const orgs = ReactiveCache.getOrgs(tpl.findOrgsOptions.get(), {
sort: { orgDisplayName: 1 },
limit: limitOrgs,
fields: { _id: true },
});
// Count only the items currently loaded to browser, not total from database
tpl.numberOrgs.set(orgs.length);
return orgs;
},
teamList() {
const tpl = Template.instance();
const limitTeams = tpl.page.get() * teamsPerPage;
const teams = ReactiveCache.getTeams(tpl.findTeamsOptions.get(), {
sort: { teamDisplayName: 1 },
limit: limitTeams,
fields: { _id: true },
});
// Count only the items currently loaded to browser, not total from database
tpl.numberTeams.set(teams.length);
return teams;
},
peopleList() {
const tpl = Template.instance();
const limitUsers = tpl.page.get() * usersPerPage;
const users = ReactiveCache.getUsers(tpl.findUsersOptions.get(), {
sort: { username: 1 },
limit: limitUsers,
fields: { _id: true },
});
// Count only the items currently loaded to browser, not total from database
tpl.numberPeople.set(users.length);
return users;
},
orgNumber() {
return Template.instance().numberOrgs.get();
},
teamNumber() {
return Template.instance().numberTeams.get();
},
peopleNumber() {
return Template.instance().numberPeople.get();
},
});
Template.people.events({
'scroll .main-body'(event, tpl) {
tpl.infiniteScrolling.checkScrollPosition(event.currentTarget, () => {
tpl.loadNextPage();
});
},
'click #searchOrgButton'(event, tpl) {
tpl.filterOrg();
},
'keydown #searchOrgInput'(event, tpl) {
if (event.keyCode === 13 && !event.shiftKey) {
tpl.filterOrg();
}
},
'click #searchTeamButton'(event, tpl) {
tpl.filterTeam();
},
'keydown #searchTeamInput'(event, tpl) {
if (event.keyCode === 13 && !event.shiftKey) {
tpl.filterTeam();
}
},
'click #searchButton'(event, tpl) {
tpl.filterPeople();
},
'click #addOrRemoveTeam'(){
document.getElementById("divAddOrRemoveTeamContainer").style.display = 'block';
},
'keydown #searchInput'(event, tpl) {
if (event.keyCode === 13 && !event.shiftKey) {
tpl.filterPeople();
}
},
'change #userFilterSelect'(event, tpl) {
const filterType = $(event.target).val();
tpl.userFilterType.set(filterType);
tpl.filterPeople();
},
'click #unlockAllUsers'(event) {
event.preventDefault();
if (confirm(TAPi18n.__('accounts-lockout-confirm-unlock-all'))) {
Meteor.call('unlockAllUsers', (error) => {
if (error) {
console.error('Error unlocking all users:', error);
} else {
// Show a brief success message
const message = document.createElement('div');
message.className = 'unlock-all-success';
message.textContent = TAPi18n.__('accounts-lockout-all-users-unlocked');
document.body.appendChild(message);
// Remove the message after a short delay
setTimeout(() => {
if (message.parentNode) {
message.parentNode.removeChild(message);
}
}, 3000);
}
});
}
},
'click #newOrgButton'() {
Popup.open('newOrg');
},
'click #newTeamButton'() {
Popup.open('newTeam');
},
'click #newUserButton'() {
Popup.open('newUser');
},
'click a.js-org-menu'(event, tpl) {
tpl.switchMenu(event);
},
'click a.js-team-menu'(event, tpl) {
tpl.switchMenu(event);
},
'click a.js-people-menu'(event, tpl) {
tpl.switchMenu(event);
},
'click a.js-locked-users-menu'(event, tpl) {
tpl.switchMenu(event);
},
});
Template.orgRow.helpers({ Template.orgRow.helpers({
orgData() { orgData() {
@ -465,259 +520,201 @@ Template.newUserPopup.helpers({
}, },
}); });
BlazeComponent.extendComponent({ Template.orgRow.events({
onCreated() {}, 'click a.edit-org': Popup.open('editOrg'),
org() { 'click a.more-settings-org': Popup.open('settingsOrg'),
return ReactiveCache.getOrg(this.orgId); });
Template.teamRow.events({
'click a.edit-team': Popup.open('editTeam'),
'click a.more-settings-team': Popup.open('settingsTeam'),
});
Template.peopleRow.events({
'click a.edit-user': Popup.open('editUser'),
'click a.more-settings-user': Popup.open('settingsUser'),
'click .selectUserChkBox': function(ev){
if(ev.currentTarget){
if(ev.currentTarget.checked){
if(!selectedUserChkBoxUserIds.includes(ev.currentTarget.id)){
selectedUserChkBoxUserIds.push(ev.currentTarget.id);
}
}
else{
if(selectedUserChkBoxUserIds.includes(ev.currentTarget.id)){
let index = selectedUserChkBoxUserIds.indexOf(ev.currentTarget.id);
if(index > -1)
selectedUserChkBoxUserIds.splice(index, 1);
}
}
}
if(selectedUserChkBoxUserIds.length > 0)
document.getElementById("divAddOrRemoveTeam").style.display = 'block';
else
document.getElementById("divAddOrRemoveTeam").style.display = 'none';
}, },
events() { 'click .js-toggle-active-status': function(ev) {
return [ ev.preventDefault();
{ const userId = this.userId;
'click a.edit-org': Popup.open('editOrg'), const user = ReactiveCache.getUser(userId);
'click a.more-settings-org': Popup.open('settingsOrg'),
}, if (!user) return;
];
// Toggle loginDisabled status
const isActive = !(user.loginDisabled === true);
// Update the user's active status
Users.update(userId, {
$set: {
loginDisabled: isActive
}
});
}, },
}).register('orgRow'); 'click .js-toggle-lock-status': function(ev){
ev.preventDefault();
const userId = this.userId;
const user = ReactiveCache.getUser(userId);
BlazeComponent.extendComponent({ if (!user) return;
onCreated() {},
team() { // Check if user is currently locked
return ReactiveCache.getTeam(this.teamId); const isLocked = user.services &&
user.services['accounts-lockout'] &&
user.services['accounts-lockout'].unlockTime &&
user.services['accounts-lockout'].unlockTime > Number(new Date());
if (isLocked) {
// Unlock the user
Meteor.call('unlockUser', userId, (error) => {
if (error) {
console.error('Error unlocking user:', error);
}
});
} else {
// Lock the user - this is optional, you may want to only allow unlocking
// If you want to implement locking too, you would need a server method for it
// For now, we'll leave this as a no-op
}
}, },
events() { });
return [
{
'click a.edit-team': Popup.open('editTeam'),
'click a.more-settings-team': Popup.open('settingsTeam'),
},
];
},
}).register('teamRow');
BlazeComponent.extendComponent({ Template.modifyTeamsUsers.helpers({
onCreated() {},
user() {
return ReactiveCache.getUser(this.userId);
},
events() {
return [
{
'click a.edit-user': Popup.open('editUser'),
'click a.more-settings-user': Popup.open('settingsUser'),
'click .selectUserChkBox': function(ev){
if(ev.currentTarget){
if(ev.currentTarget.checked){
if(!selectedUserChkBoxUserIds.includes(ev.currentTarget.id)){
selectedUserChkBoxUserIds.push(ev.currentTarget.id);
}
}
else{
if(selectedUserChkBoxUserIds.includes(ev.currentTarget.id)){
let index = selectedUserChkBoxUserIds.indexOf(ev.currentTarget.id);
if(index > -1)
selectedUserChkBoxUserIds.splice(index, 1);
}
}
}
if(selectedUserChkBoxUserIds.length > 0)
document.getElementById("divAddOrRemoveTeam").style.display = 'block';
else
document.getElementById("divAddOrRemoveTeam").style.display = 'none';
},
'click .js-toggle-active-status': function(ev) {
ev.preventDefault();
const userId = this.userId;
const user = ReactiveCache.getUser(userId);
if (!user) return;
// Toggle loginDisabled status
const isActive = !(user.loginDisabled === true);
// Update the user's active status
Users.update(userId, {
$set: {
loginDisabled: isActive
}
});
},
'click .js-toggle-lock-status': function(ev){
ev.preventDefault();
const userId = this.userId;
const user = ReactiveCache.getUser(userId);
if (!user) return;
// Check if user is currently locked
const isLocked = user.services &&
user.services['accounts-lockout'] &&
user.services['accounts-lockout'].unlockTime &&
user.services['accounts-lockout'].unlockTime > Number(new Date());
if (isLocked) {
// Unlock the user
Meteor.call('unlockUser', userId, (error) => {
if (error) {
console.error('Error unlocking user:', error);
}
});
} else {
// Lock the user - this is optional, you may want to only allow unlocking
// If you want to implement locking too, you would need a server method for it
// For now, we'll leave this as a no-op
}
},
},
];
},
}).register('peopleRow');
BlazeComponent.extendComponent({
onCreated() {},
teamsDatas() { teamsDatas() {
const ret = ReactiveCache.getTeams({}, {sort: { teamDisplayName: 1 }}); const ret = ReactiveCache.getTeams({}, {sort: { teamDisplayName: 1 }});
return ret; return ret;
}, },
events() { });
return [
{
'click #cancelBtn': function(){
let selectedElt = document.getElementById("jsteamsUser");
document.getElementById("divAddOrRemoveTeamContainer").style.display = 'none';
},
'click #addTeamBtn': function(){
let selectedElt;
let selectedEltValue;
let selectedEltValueId;
let userTms = [];
let currentUser;
let currUserTeamIndex;
selectedElt = document.getElementById("jsteamsUser"); Template.modifyTeamsUsers.events({
selectedEltValue = selectedElt.options[selectedElt.selectedIndex].text; 'click #cancelBtn': function(){
selectedEltValueId = selectedElt.options[selectedElt.selectedIndex].value; let selectedElt = document.getElementById("jsteamsUser");
document.getElementById("divAddOrRemoveTeamContainer").style.display = 'none';
},
'click #addTeamBtn': function(){
let selectedElt;
let selectedEltValue;
let selectedEltValueId;
let userTms = [];
let currentUser;
let currUserTeamIndex;
if(document.getElementById('addAction').checked){ selectedElt = document.getElementById("jsteamsUser");
for(let i = 0; i < selectedUserChkBoxUserIds.length; i++){ selectedEltValue = selectedElt.options[selectedElt.selectedIndex].text;
currentUser = ReactiveCache.getUser(selectedUserChkBoxUserIds[i]); selectedEltValueId = selectedElt.options[selectedElt.selectedIndex].value;
userTms = currentUser.teams;
if(userTms == undefined || userTms.length == 0){
userTms = [];
userTms.push({
"teamId": selectedEltValueId,
"teamDisplayName": selectedEltValue,
})
}
else if(userTms.length > 0)
{
currUserTeamIndex = userTms.findIndex(function(t){ return t.teamId == selectedEltValueId});
if(currUserTeamIndex == -1){
userTms.push({
"teamId": selectedEltValueId,
"teamDisplayName": selectedEltValue,
});
}
}
Users.update(selectedUserChkBoxUserIds[i], { if(document.getElementById('addAction').checked){
$set:{ for(let i = 0; i < selectedUserChkBoxUserIds.length; i++){
teams: userTms currentUser = ReactiveCache.getUser(selectedUserChkBoxUserIds[i]);
} userTms = currentUser.teams;
}); if(userTms == undefined || userTms.length == 0){
} userTms = [];
userTms.push({
"teamId": selectedEltValueId,
"teamDisplayName": selectedEltValue,
})
}
else if(userTms.length > 0)
{
currUserTeamIndex = userTms.findIndex(function(t){ return t.teamId == selectedEltValueId});
if(currUserTeamIndex == -1){
userTms.push({
"teamId": selectedEltValueId,
"teamDisplayName": selectedEltValue,
});
} }
else{ }
for(let i = 0; i < selectedUserChkBoxUserIds.length; i++){
currentUser = ReactiveCache.getUser(selectedUserChkBoxUserIds[i]);
userTms = currentUser.teams;
if(userTms !== undefined || userTms.length > 0)
{
currUserTeamIndex = userTms.findIndex(function(t){ return t.teamId == selectedEltValueId});
if(currUserTeamIndex != -1){
userTms.splice(currUserTeamIndex, 1);
}
}
Users.update(selectedUserChkBoxUserIds[i], { Users.update(selectedUserChkBoxUserIds[i], {
$set:{ $set:{
teams: userTms teams: userTms
}
});
}
} }
});
document.getElementById("divAddOrRemoveTeamContainer").style.display = 'none'; }
}, }
}, else{
]; for(let i = 0; i < selectedUserChkBoxUserIds.length; i++){
}, currentUser = ReactiveCache.getUser(selectedUserChkBoxUserIds[i]);
}).register('modifyTeamsUsers'); userTms = currentUser.teams;
if(userTms !== undefined || userTms.length > 0)
BlazeComponent.extendComponent({ {
events() { currUserTeamIndex = userTms.findIndex(function(t){ return t.teamId == selectedEltValueId});
return [ if(currUserTeamIndex != -1){
{ userTms.splice(currUserTeamIndex, 1);
'click a.new-org': Popup.open('newOrg'),
},
];
},
}).register('newOrgRow');
BlazeComponent.extendComponent({
events() {
return [
{
'click a.new-team': Popup.open('newTeam'),
},
];
},
}).register('newTeamRow');
BlazeComponent.extendComponent({
events() {
return [
{
'click a.new-user': Popup.open('newUser'),
},
];
},
}).register('newUserRow');
BlazeComponent.extendComponent({
events() {
return [
{
'click .allUserChkBox': function(ev){
selectedUserChkBoxUserIds = [];
const checkboxes = document.getElementsByClassName("selectUserChkBox");
if(ev.currentTarget){
if(ev.currentTarget.checked){
for (let i=0; i<checkboxes.length; i++) {
if (!checkboxes[i].disabled) {
selectedUserChkBoxUserIds.push(checkboxes[i].id);
checkboxes[i].checked = true;
}
}
}
else{
for (let i=0; i<checkboxes.length; i++) {
if (!checkboxes[i].disabled) {
checkboxes[i].checked = false;
}
}
}
} }
}
if(selectedUserChkBoxUserIds.length > 0) Users.update(selectedUserChkBoxUserIds[i], {
document.getElementById("divAddOrRemoveTeam").style.display = 'block'; $set:{
else teams: userTms
document.getElementById("divAddOrRemoveTeam").style.display = 'none'; }
}, });
}, }
]; }
document.getElementById("divAddOrRemoveTeamContainer").style.display = 'none';
}, },
}).register('selectAllUser'); });
Template.newOrgRow.events({
'click a.new-org': Popup.open('newOrg'),
});
Template.newTeamRow.events({
'click a.new-team': Popup.open('newTeam'),
});
Template.newUserRow.events({
'click a.new-user': Popup.open('newUser'),
});
Template.selectAllUser.events({
'click .allUserChkBox': function(ev){
selectedUserChkBoxUserIds = [];
const checkboxes = document.getElementsByClassName("selectUserChkBox");
if(ev.currentTarget){
if(ev.currentTarget.checked){
for (let i=0; i<checkboxes.length; i++) {
if (!checkboxes[i].disabled) {
selectedUserChkBoxUserIds.push(checkboxes[i].id);
checkboxes[i].checked = true;
}
}
}
else{
for (let i=0; i<checkboxes.length; i++) {
if (!checkboxes[i].disabled) {
checkboxes[i].checked = false;
}
}
}
}
if(selectedUserChkBoxUserIds.length > 0)
document.getElementById("divAddOrRemoveTeam").style.display = 'block';
else
document.getElementById("divAddOrRemoveTeam").style.display = 'none';
},
});
Template.editOrgPopup.events({ Template.editOrgPopup.events({
submit(event, templateInstance) { submit(event, templateInstance) {

File diff suppressed because it is too large Load diff

View file

@ -1,111 +1,118 @@
import { ReactiveCache } from '/imports/reactiveCache'; import { ReactiveCache } from '/imports/reactiveCache';
import { InfiniteScrolling } from '/client/lib/infiniteScrolling';
const translationsPerPage = 25; const translationsPerPage = 25;
BlazeComponent.extendComponent({ Template.translation.onCreated(function () {
mixins() { this.error = new ReactiveVar('');
return [Mixins.InfiniteScrolling]; this.loading = new ReactiveVar(false);
}, this.translationSetting = new ReactiveVar(true);
onCreated() { this.findTranslationsOptions = new ReactiveVar({});
this.error = new ReactiveVar(''); this.numberTranslations = new ReactiveVar(0);
this.loading = new ReactiveVar(false);
this.translationSetting = new ReactiveVar(true);
this.findTranslationsOptions = new ReactiveVar({});
this.numberTranslations = new ReactiveVar(0);
this.page = new ReactiveVar(1); this.page = new ReactiveVar(1);
this.loadNextPageLocked = false; this.loadNextPageLocked = false;
this.callFirstWith(null, 'resetNextPeak'); this.infiniteScrolling = new InfiniteScrolling();
this.autorun(() => {
const limitTranslations = this.page.get() * translationsPerPage;
this.subscribe('translation', this.findTranslationsOptions.get(), 0, () => { this.loadNextPage = () => {
this.loadNextPageLocked = false;
const nextPeakBefore = this.callFirstWith(null, 'getNextPeak');
this.calculateNextPeak();
const nextPeakAfter = this.callFirstWith(null, 'getNextPeak');
if (nextPeakBefore === nextPeakAfter) {
this.callFirstWith(null, 'resetNextPeak');
}
});
});
},
events() {
return [
{
'click #searchTranslationButton'() {
this.filterTranslation();
},
'keydown #searchTranslationInput'(event) {
if (event.keyCode === 13 && !event.shiftKey) {
this.filterTranslation();
}
},
'click #newTranslationButton'() {
Popup.open('newTranslation');
},
'click a.js-translation-menu': this.switchMenu,
},
];
},
filterTranslation() {
const value = $('#searchTranslationInput').first().val();
if (value === '') {
this.findTranslationsOptions.set({});
} else {
const regex = new RegExp(value, 'i');
this.findTranslationsOptions.set({
$or: [
{ language: regex },
{ text: regex },
{ translationText: regex },
],
});
}
},
loadNextPage() {
if (this.loadNextPageLocked === false) { if (this.loadNextPageLocked === false) {
this.page.set(this.page.get() + 1); this.page.set(this.page.get() + 1);
this.loadNextPageLocked = true; this.loadNextPageLocked = true;
} }
}, };
calculateNextPeak() {
this.calculateNextPeak = () => {
const element = this.find('.main-body'); const element = this.find('.main-body');
if (element) { if (element) {
const altitude = element.scrollHeight; this.infiniteScrolling.setNextPeak(element.scrollHeight);
this.callFirstWith(this, 'setNextPeak', altitude);
} }
};
this.autorun(() => {
const limitTranslations = this.page.get() * translationsPerPage;
this.subscribe('translation', this.findTranslationsOptions.get(), 0, () => {
this.loadNextPageLocked = false;
const nextPeakBefore = this.infiniteScrolling.getNextPeak();
this.calculateNextPeak();
const nextPeakAfter = this.infiniteScrolling.getNextPeak();
if (nextPeakBefore === nextPeakAfter) {
this.infiniteScrolling.resetNextPeak();
}
});
});
});
Template.translation.helpers({
loading() {
return Template.instance().loading;
}, },
reachNextPeak() { translationSetting() {
this.loadNextPage(); return Template.instance().translationSetting;
},
setError(error) {
this.error.set(error);
},
setLoading(w) {
this.loading.set(w);
}, },
translationList() { translationList() {
const translations = ReactiveCache.getTranslations(this.findTranslationsOptions.get(), { const tpl = Template.instance();
const translations = ReactiveCache.getTranslations(tpl.findTranslationsOptions.get(), {
sort: { modifiedAt: 1 }, sort: { modifiedAt: 1 },
fields: { _id: true }, fields: { _id: true },
}); });
this.numberTranslations.set(translations.length); tpl.numberTranslations.set(translations.length);
return translations; return translations;
}, },
translationNumber() { translationNumber() {
return this.numberTranslations.get(); return Template.instance().numberTranslations.get();
}, },
switchMenu(event) { setError(error) {
Template.instance().error.set(error);
},
setLoading(w) {
Template.instance().loading.set(w);
},
});
Template.translation.events({
'click #searchTranslationButton'(event, tpl) {
_filterTranslation(tpl);
},
'keydown #searchTranslationInput'(event, tpl) {
if (event.keyCode === 13 && !event.shiftKey) {
_filterTranslation(tpl);
}
},
'click #newTranslationButton'() {
Popup.open('newTranslation');
},
'click a.js-translation-menu'(event, tpl) {
const target = $(event.target); const target = $(event.target);
if (!target.hasClass('active')) { if (!target.hasClass('active')) {
$('.side-menu li.active').removeClass('active'); $('.side-menu li.active').removeClass('active');
target.parent().addClass('active'); target.parent().addClass('active');
const targetID = target.data('id'); const targetID = target.data('id');
this.translationSetting.set('translation-setting' === targetID); tpl.translationSetting.set('translation-setting' === targetID);
} }
}, },
}).register('translation'); 'scroll .main-body'(event, tpl) {
tpl.infiniteScrolling.checkScrollPosition(event.currentTarget, () => {
tpl.loadNextPage();
});
},
});
function _filterTranslation(tpl) {
const value = $('#searchTranslationInput').first().val();
if (value === '') {
tpl.findTranslationsOptions.set({});
} else {
const regex = new RegExp(value, 'i');
tpl.findTranslationsOptions.set({
$or: [
{ language: regex },
{ text: regex },
{ translationText: regex },
],
});
}
}
Template.translationRow.helpers({ Template.translationRow.helpers({
translationData() { translationData() {
@ -135,30 +142,20 @@ Template.newTranslationPopup.helpers({
}, },
}); });
BlazeComponent.extendComponent({ Template.translationRow.helpers({
onCreated() {},
translation() { translation() {
return ReactiveCache.getTranslation(this.translationId); return ReactiveCache.getTranslation(this.translationId);
}, },
events() { });
return [
{
'click a.edit-translation': Popup.open('editTranslation'),
'click a.more-settings-translation': Popup.open('settingsTranslation'),
},
];
},
}).register('translationRow');
BlazeComponent.extendComponent({ Template.translationRow.events({
events() { 'click a.edit-translation': Popup.open('editTranslation'),
return [ 'click a.more-settings-translation': Popup.open('settingsTranslation'),
{ });
'click a.new-translation': Popup.open('newTranslation'),
}, Template.newTranslationRow.events({
]; 'click a.new-translation': Popup.open('newTranslation'),
}, });
}).register('newTranslationRow');
Template.editTranslationPopup.events({ Template.editTranslationPopup.events({
submit(event, templateInstance) { submit(event, templateInstance) {

File diff suppressed because it is too large Load diff

View file

@ -1,28 +1,84 @@
import { ReactiveCache } from '/imports/reactiveCache'; import { ReactiveCache } from '/imports/reactiveCache';
import { TAPi18n } from '/imports/i18n'; import { TAPi18n } from '/imports/i18n';
import Lists from '/models/lists';
import Swimlanes from '/models/swimlanes';
//archivedRequested = false; //archivedRequested = false;
const subManager = new SubsManager(); const subManager = new SubsManager();
BlazeComponent.extendComponent({ function getArchivedCards() {
onCreated() { const ret = ReactiveCache.getCards(
this.isArchiveReady = new ReactiveVar(false); {
archived: true,
boardId: Session.get('currentBoard'),
},
{
sort: { archivedAt: -1, modifiedAt: -1 },
},
);
return ret;
}
// The pattern we use to manually handle data loading is described here: function getArchivedLists() {
// https://kadira.io/academy/meteor-routing-guide/content/subscriptions-and-data-management/using-subs-manager return ReactiveCache.getLists(
// XXX The boardId should be readed from some sort the component "props", {
// unfortunatly, Blaze doesn't have this notion. archived: true,
this.autorun(() => { boardId: Session.get('currentBoard'),
const currentBoardId = Session.get('currentBoard'); },
if (!currentBoardId) return; {
const handle = subManager.subscribe('board', currentBoardId, true); sort: { archivedAt: -1, modifiedAt: -1 },
//archivedRequested = true; },
Tracker.nonreactive(() => { );
Tracker.autorun(() => { }
this.isArchiveReady.set(handle.ready());
}); function getArchivedSwimlanes() {
return ReactiveCache.getSwimlanes(
{
archived: true,
boardId: Session.get('currentBoard'),
},
{
sort: { archivedAt: -1, modifiedAt: -1 },
},
);
}
Template.archivesSidebar.onCreated(function () {
this.isArchiveReady = new ReactiveVar(false);
// The pattern we use to manually handle data loading is described here:
// https://kadira.io/academy/meteor-routing-guide/content/subscriptions-and-data-management/using-subs-manager
// XXX The boardId should be readed from some sort the component "props",
// unfortunatly, Blaze doesn't have this notion.
this.autorun(() => {
const currentBoardId = Session.get('currentBoard');
if (!currentBoardId) return;
const handle = subManager.subscribe('board', currentBoardId, true);
//archivedRequested = true;
Tracker.nonreactive(() => {
Tracker.autorun(() => {
this.isArchiveReady.set(handle.ready());
}); });
}); });
});
});
Template.archivesSidebar.onRendered(function () {
// XXX We should support dragging a card from the sidebar to the board
});
Template.archivesSidebar.helpers({
isArchiveReady() {
return Template.instance().isArchiveReady;
},
isBoardAdmin() {
return ReactiveCache.getCurrentUser().isBoardAdmin();
},
isWorker() {
const currentBoard = Utils.getCurrentBoard();
return (
!currentBoard.hasAdmin(this.userId) && currentBoard.hasWorker(this.userId)
);
}, },
tabs() { tabs() {
@ -34,139 +90,98 @@ BlazeComponent.extendComponent({
}, },
archivedCards() { archivedCards() {
const ret = ReactiveCache.getCards( return getArchivedCards();
{
archived: true,
boardId: Session.get('currentBoard'),
},
{
sort: { archivedAt: -1, modifiedAt: -1 },
},
);
return ret;
}, },
archivedLists() { archivedLists() {
return ReactiveCache.getLists( return getArchivedLists();
{
archived: true,
boardId: Session.get('currentBoard'),
},
{
sort: { archivedAt: -1, modifiedAt: -1 },
},
);
}, },
archivedSwimlanes() { archivedSwimlanes() {
return ReactiveCache.getSwimlanes( return getArchivedSwimlanes();
{
archived: true,
boardId: Session.get('currentBoard'),
},
{
sort: { archivedAt: -1, modifiedAt: -1 },
},
);
}, },
cardIsInArchivedList() { cardIsInArchivedList() {
return this.currentData().list().archived; return Template.currentData().list().archived;
},
onRendered() {
// XXX We should support dragging a card from the sidebar to the board
},
events() {
return [
{
async 'click .js-restore-card'() {
const card = this.currentData();
if (card.canBeRestored()) {
await card.restore();
}
},
async 'click .js-restore-all-cards'() {
for (const card of this.archivedCards()) {
if (card.canBeRestored()) {
await card.restore();
}
}
},
'click .js-delete-card': Popup.afterConfirm('cardDelete', async function() {
const cardId = this._id;
await Cards.removeAsync(cardId);
Popup.back();
}),
'click .js-delete-all-cards': Popup.afterConfirm('cardDelete', async () => {
for (const card of this.archivedCards()) {
await Cards.removeAsync(card._id);
}
Popup.back();
}),
async 'click .js-restore-list'() {
const list = this.currentData();
await list.restore();
},
async 'click .js-restore-all-lists'() {
for (const list of this.archivedLists()) {
await list.restore();
}
},
'click .js-delete-list': Popup.afterConfirm('listDelete', async function() {
await this.remove();
Popup.back();
}),
'click .js-delete-all-lists': Popup.afterConfirm('listDelete', async () => {
for (const list of this.archivedLists()) {
await list.remove();
}
Popup.back();
}),
async 'click .js-restore-swimlane'() {
const swimlane = this.currentData();
await swimlane.restore();
},
async 'click .js-restore-all-swimlanes'() {
for (const swimlane of this.archivedSwimlanes()) {
await swimlane.restore();
}
},
'click .js-delete-swimlane': Popup.afterConfirm(
'swimlaneDelete',
async function() {
await this.remove();
Popup.back();
},
),
'click .js-delete-all-swimlanes': Popup.afterConfirm(
'swimlaneDelete',
async () => {
for (const swimlane of this.archivedSwimlanes()) {
await swimlane.remove();
}
Popup.back();
},
),
},
];
},
}).register('archivesSidebar');
Template.archivesSidebar.helpers({
isBoardAdmin() {
return ReactiveCache.getCurrentUser().isBoardAdmin();
},
isWorker() {
const currentBoard = Utils.getCurrentBoard();
return (
!currentBoard.hasAdmin(this.userId) && currentBoard.hasWorker(this.userId)
);
}, },
}); });
Template.archivesSidebar.events({
async 'click .js-restore-card'() {
const card = Template.currentData();
if (card.canBeRestored()) {
await card.restore();
}
},
async 'click .js-restore-all-cards'() {
for (const card of getArchivedCards()) {
if (card.canBeRestored()) {
await card.restore();
}
}
},
'click .js-delete-card': Popup.afterConfirm('cardDelete', async function() {
const cardId = this._id;
await Cards.removeAsync(cardId);
Popup.back();
}),
'click .js-delete-all-cards': Popup.afterConfirm('cardDelete', async () => {
for (const card of getArchivedCards()) {
await Cards.removeAsync(card._id);
}
Popup.back();
}),
async 'click .js-restore-list'() {
const data = Template.currentData();
const list = Lists.findOne(data._id) || data;
await list.restore();
},
async 'click .js-restore-all-lists'() {
for (const list of getArchivedLists()) {
await list.restore();
}
},
'click .js-delete-list': Popup.afterConfirm('listDelete', async function() {
const list = Lists.findOne(this._id);
if (list) await list.remove();
Popup.back();
}),
'click .js-delete-all-lists': Popup.afterConfirm('listDelete', async () => {
for (const list of getArchivedLists()) {
await list.remove();
}
Popup.back();
}),
async 'click .js-restore-swimlane'() {
const data = Template.currentData();
const swimlane = Swimlanes.findOne(data._id) || data;
await swimlane.restore();
},
async 'click .js-restore-all-swimlanes'() {
for (const swimlane of getArchivedSwimlanes()) {
await swimlane.restore();
}
},
'click .js-delete-swimlane': Popup.afterConfirm(
'swimlaneDelete',
async function() {
const swimlane = Swimlanes.findOne(this._id);
if (swimlane) await swimlane.remove();
Popup.back();
},
),
'click .js-delete-all-swimlanes': Popup.afterConfirm(
'swimlaneDelete',
async () => {
for (const swimlane of getArchivedSwimlanes()) {
await swimlane.remove();
}
Popup.back();
},
),
});

View file

@ -1,111 +1,119 @@
import { ReactiveCache } from '/imports/reactiveCache'; import { ReactiveCache } from '/imports/reactiveCache';
import { TAPi18n } from '/imports/i18n'; import { TAPi18n } from '/imports/i18n';
BlazeComponent.extendComponent({ Template.customFieldsSidebar.helpers({
customFields() { customFields() {
const ret = ReactiveCache.getCustomFields({ const ret = ReactiveCache.getCustomFields({
boardIds: { $in: [Session.get('currentBoard')] }, boardIds: { $in: [Session.get('currentBoard')] },
}); });
return ret; return ret;
}, },
});
events() { Template.customFieldsSidebar.events({
return [ 'click .js-open-create-custom-field': Popup.open('createCustomField'),
{ 'click .js-edit-custom-field': Popup.open('editCustomField'),
'click .js-open-create-custom-field': Popup.open('createCustomField'), });
'click .js-edit-custom-field': Popup.open('editCustomField'),
},
];
},
}).register('customFieldsSidebar');
const CreateCustomFieldPopup = BlazeComponent.extendComponent({ const CUSTOM_FIELD_TYPES = [
_types: [ 'text',
'text', 'number',
'number', 'date',
'date', 'dropdown',
'dropdown', 'currency',
'currency', 'checkbox',
'checkbox', 'stringtemplate',
'stringtemplate', ];
],
_currencyList: [ const CURRENCY_LIST = [
{ { name: 'US Dollar', code: 'USD' },
name: 'US Dollar', { name: 'Euro', code: 'EUR' },
code: 'USD', { name: 'Yen', code: 'JPY' },
}, { name: 'Pound Sterling', code: 'GBP' },
{ { name: 'Australian Dollar', code: 'AUD' },
name: 'Euro', { name: 'Canadian Dollar', code: 'CAD' },
code: 'EUR', { name: 'Swiss Franc', code: 'CHF' },
}, { name: 'Yuan Renminbi', code: 'CNY' },
{ { name: 'Hong Kong Dollar', code: 'HKD' },
name: 'Yen', { name: 'New Zealand Dollar', code: 'NZD' },
code: 'JPY', ];
},
{
name: 'Pound Sterling',
code: 'GBP',
},
{
name: 'Australian Dollar',
code: 'AUD',
},
{
name: 'Canadian Dollar',
code: 'CAD',
},
{
name: 'Swiss Franc',
code: 'CHF',
},
{
name: 'Yuan Renminbi',
code: 'CNY',
},
{
name: 'Hong Kong Dollar',
code: 'HKD',
},
{
name: 'New Zealand Dollar',
code: 'NZD',
},
],
onCreated() { function getDropdownItems(tpl) {
this.type = new ReactiveVar( const items = tpl.dropdownItems.get();
this.data().type ? this.data().type : this._types[0], Array.from(tpl.findAll('.js-field-settings-dropdown input')).forEach(
); (el, index) => {
if (!items[index])
items[index] = {
_id: Random.id(6),
};
items[index].name = el.value.trim();
},
);
return items;
}
this.currencyCode = new ReactiveVar( function getSettings(tpl) {
this.data().settings && this.data().settings.currencyCode const settings = {};
? this.data().settings.currencyCode switch (tpl.type.get()) {
: this._currencyList[0].code, case 'currency': {
); const currencyCode = tpl.currencyCode.get();
settings.currencyCode = currencyCode;
break;
}
case 'dropdown': {
const dropdownItems = getDropdownItems(tpl).filter(
item => !!item.name.trim(),
);
settings.dropdownItems = dropdownItems;
break;
}
case 'stringtemplate': {
const stringtemplateFormat = tpl.stringtemplateFormat.get();
settings.stringtemplateFormat = stringtemplateFormat;
this.dropdownItems = new ReactiveVar( const stringtemplateSeparator = tpl.stringtemplateSeparator.get();
this.data().settings && this.data().settings.dropdownItems settings.stringtemplateSeparator = stringtemplateSeparator;
? this.data().settings.dropdownItems break;
: [], }
); }
return settings;
}
this.stringtemplateFormat = new ReactiveVar( Template.createCustomFieldPopup.onCreated(function () {
this.data().settings && this.data().settings.stringtemplateFormat const data = Template.currentData();
? this.data().settings.stringtemplateFormat this.type = new ReactiveVar(
: '', data.type ? data.type : CUSTOM_FIELD_TYPES[0],
); );
this.stringtemplateSeparator = new ReactiveVar( this.currencyCode = new ReactiveVar(
this.data().settings && this.data().settings.stringtemplateSeparator data.settings && data.settings.currencyCode
? this.data().settings.stringtemplateSeparator ? data.settings.currencyCode
: '', : CURRENCY_LIST[0].code,
); );
},
this.dropdownItems = new ReactiveVar(
data.settings && data.settings.dropdownItems
? data.settings.dropdownItems
: [],
);
this.stringtemplateFormat = new ReactiveVar(
data.settings && data.settings.stringtemplateFormat
? data.settings.stringtemplateFormat
: '',
);
this.stringtemplateSeparator = new ReactiveVar(
data.settings && data.settings.stringtemplateSeparator
? data.settings.stringtemplateSeparator
: '',
);
});
Template.createCustomFieldPopup.helpers({
types() { types() {
const currentType = this.data().type; const currentType = Template.currentData().type;
return this._types.map(type => { return CUSTOM_FIELD_TYPES.map(type => {
return { return {
value: type, value: type,
name: TAPi18n.__(`custom-field-${type}`), name: TAPi18n.__(`custom-field-${type}`),
@ -115,13 +123,13 @@ const CreateCustomFieldPopup = BlazeComponent.extendComponent({
}, },
isTypeNotSelected(type) { isTypeNotSelected(type) {
return this.type.get() !== type; return Template.instance().type.get() !== type;
}, },
getCurrencyCodes() { getCurrencyCodes() {
const currentCode = this.currencyCode.get(); const currentCode = Template.instance().currencyCode.get();
return this._currencyList.map(({ name, code }) => { return CURRENCY_LIST.map(({ name, code }) => {
return { return {
name: `${code} - ${name}`, name: `${code} - ${name}`,
value: code, value: code,
@ -131,176 +139,128 @@ const CreateCustomFieldPopup = BlazeComponent.extendComponent({
}, },
getDropdownItems() { getDropdownItems() {
const items = this.dropdownItems.get(); return getDropdownItems(Template.instance());
Array.from(this.findAll('.js-field-settings-dropdown input')).forEach(
(el, index) => {
//console.log('each item!', index, el.value);
if (!items[index])
items[index] = {
_id: Random.id(6),
};
items[index].name = el.value.trim();
},
);
return items;
}, },
getStringtemplateFormat() { getStringtemplateFormat() {
return this.stringtemplateFormat.get(); return Template.instance().stringtemplateFormat.get();
}, },
getStringtemplateSeparator() { getStringtemplateSeparator() {
return this.stringtemplateSeparator.get(); return Template.instance().stringtemplateSeparator.get();
},
getSettings() {
const settings = {};
switch (this.type.get()) {
case 'currency': {
const currencyCode = this.currencyCode.get();
settings.currencyCode = currencyCode;
break;
}
case 'dropdown': {
const dropdownItems = this.getDropdownItems().filter(
item => !!item.name.trim(),
);
settings.dropdownItems = dropdownItems;
break;
}
case 'stringtemplate': {
const stringtemplateFormat = this.stringtemplateFormat.get();
settings.stringtemplateFormat = stringtemplateFormat;
const stringtemplateSeparator = this.stringtemplateSeparator.get();
settings.stringtemplateSeparator = stringtemplateSeparator;
break;
}
}
return settings;
},
events() {
return [
{
'change .js-field-type'(evt) {
const value = evt.target.value;
this.type.set(value);
},
'change .js-field-currency'(evt) {
const value = evt.target.value;
this.currencyCode.set(value);
},
'keydown .js-dropdown-item.last'(evt) {
if (evt.target.value.trim() && evt.keyCode === 13) {
const items = this.getDropdownItems();
this.dropdownItems.set(items);
evt.target.value = '';
}
},
'input .js-field-stringtemplate-format'(evt) {
const value = evt.target.value;
this.stringtemplateFormat.set(value);
},
'input .js-field-stringtemplate-separator'(evt) {
const value = evt.target.value;
this.stringtemplateSeparator.set(value);
},
'click .js-field-show-on-card'(evt) {
let $target = $(evt.target);
if (!$target.hasClass('js-field-show-on-card')) {
$target = $target.parent();
}
$target.find('.materialCheckBox').toggleClass('is-checked');
$target.toggleClass('is-checked');
},
'click .js-field-automatically-on-card'(evt) {
let $target = $(evt.target);
if (!$target.hasClass('js-field-automatically-on-card')) {
$target = $target.parent();
}
$target.find('.materialCheckBox').toggleClass('is-checked');
$target.toggleClass('is-checked');
},
'click .js-field-always-on-card'(evt) {
let $target = $(evt.target);
if (!$target.hasClass('js-field-always-on-card')) {
$target = $target.parent();
}
$target.find('.materialCheckBox').toggleClass('is-checked');
$target.toggleClass('is-checked');
},
'click .js-field-showLabel-on-card'(evt) {
let $target = $(evt.target);
if (!$target.hasClass('js-field-showLabel-on-card')) {
$target = $target.parent();
}
$target.find('.materialCheckBox').toggleClass('is-checked');
$target.toggleClass('is-checked');
},
'click .js-field-show-sum-at-top-of-list'(evt) {
let $target = $(evt.target);
if (!$target.hasClass('js-field-show-sum-at-top-of-list')) {
$target = $target.parent();
}
$target.find('.materialCheckBox').toggleClass('is-checked');
$target.toggleClass('is-checked');
},
'click .primary'(evt) {
evt.preventDefault();
const data = {
name: this.find('.js-field-name').value.trim(),
type: this.type.get(),
settings: this.getSettings(),
showOnCard: this.find('.js-field-show-on-card.is-checked') !== null,
showLabelOnMiniCard:
this.find('.js-field-showLabel-on-card.is-checked') !== null,
automaticallyOnCard:
this.find('.js-field-automatically-on-card.is-checked') !== null,
alwaysOnCard:
this.find('.js-field-always-on-card.is-checked') !== null,
showSumAtTopOfList:
this.find('.js-field-show-sum-at-top-of-list.is-checked') !== null,
};
// insert or update
if (!this.data()._id) {
data.boardIds = [Session.get('currentBoard')];
CustomFields.insert(data);
} else {
CustomFields.update(this.data()._id, { $set: data });
}
Popup.back();
},
'click .js-delete-custom-field': Popup.afterConfirm(
'deleteCustomField',
function() {
const customField = ReactiveCache.getCustomField(this._id);
if (customField.boardIds.length > 1) {
CustomFields.update(customField._id, {
$pull: {
boardIds: Session.get('currentBoard'),
},
});
} else {
CustomFields.remove(customField._id);
}
Popup.back();
},
),
},
];
}, },
}); });
CreateCustomFieldPopup.register('createCustomFieldPopup');
(class extends CreateCustomFieldPopup { Template.createCustomFieldPopup.events({
template() { 'change .js-field-type'(evt, tpl) {
return 'createCustomFieldPopup'; const value = evt.target.value;
} tpl.type.set(value);
}.register('editCustomFieldPopup')); },
'change .js-field-currency'(evt, tpl) {
const value = evt.target.value;
tpl.currencyCode.set(value);
},
'keydown .js-dropdown-item.last'(evt, tpl) {
if (evt.target.value.trim() && evt.keyCode === 13) {
const items = getDropdownItems(tpl);
tpl.dropdownItems.set(items);
evt.target.value = '';
}
},
'input .js-field-stringtemplate-format'(evt, tpl) {
const value = evt.target.value;
tpl.stringtemplateFormat.set(value);
},
'input .js-field-stringtemplate-separator'(evt, tpl) {
const value = evt.target.value;
tpl.stringtemplateSeparator.set(value);
},
'click .js-field-show-on-card'(evt) {
let $target = $(evt.target);
if (!$target.hasClass('js-field-show-on-card')) {
$target = $target.parent();
}
$target.find('.materialCheckBox').toggleClass('is-checked');
$target.toggleClass('is-checked');
},
'click .js-field-automatically-on-card'(evt) {
let $target = $(evt.target);
if (!$target.hasClass('js-field-automatically-on-card')) {
$target = $target.parent();
}
$target.find('.materialCheckBox').toggleClass('is-checked');
$target.toggleClass('is-checked');
},
'click .js-field-always-on-card'(evt) {
let $target = $(evt.target);
if (!$target.hasClass('js-field-always-on-card')) {
$target = $target.parent();
}
$target.find('.materialCheckBox').toggleClass('is-checked');
$target.toggleClass('is-checked');
},
'click .js-field-showLabel-on-card'(evt) {
let $target = $(evt.target);
if (!$target.hasClass('js-field-showLabel-on-card')) {
$target = $target.parent();
}
$target.find('.materialCheckBox').toggleClass('is-checked');
$target.toggleClass('is-checked');
},
'click .js-field-show-sum-at-top-of-list'(evt) {
let $target = $(evt.target);
if (!$target.hasClass('js-field-show-sum-at-top-of-list')) {
$target = $target.parent();
}
$target.find('.materialCheckBox').toggleClass('is-checked');
$target.toggleClass('is-checked');
},
'click .primary'(evt, tpl) {
evt.preventDefault();
const data = {
name: tpl.find('.js-field-name').value.trim(),
type: tpl.type.get(),
settings: getSettings(tpl),
showOnCard: tpl.find('.js-field-show-on-card.is-checked') !== null,
showLabelOnMiniCard:
tpl.find('.js-field-showLabel-on-card.is-checked') !== null,
automaticallyOnCard:
tpl.find('.js-field-automatically-on-card.is-checked') !== null,
alwaysOnCard:
tpl.find('.js-field-always-on-card.is-checked') !== null,
showSumAtTopOfList:
tpl.find('.js-field-show-sum-at-top-of-list.is-checked') !== null,
};
const currentData = Template.currentData();
// insert or update
if (!currentData._id) {
data.boardIds = [Session.get('currentBoard')];
CustomFields.insert(data);
} else {
CustomFields.update(currentData._id, { $set: data });
}
Popup.back();
},
'click .js-delete-custom-field': Popup.afterConfirm(
'deleteCustomField',
function() {
const customField = ReactiveCache.getCustomField(this._id);
if (customField.boardIds.length > 1) {
CustomFields.update(customField._id, {
$pull: {
boardIds: Session.get('currentBoard'),
},
});
} else {
CustomFields.remove(customField._id);
}
Popup.back();
},
),
});
/*Template.deleteCustomFieldPopup.events({ /*Template.deleteCustomFieldPopup.events({
'submit'(evt) { 'submit'(evt) {

View file

@ -3,108 +3,102 @@ import { TAPi18n } from '/imports/i18n';
const subManager = new SubsManager(); const subManager = new SubsManager();
BlazeComponent.extendComponent({ Template.filterSidebar.events({
events() { 'submit .js-list-filter'(evt, tpl) {
return [ evt.preventDefault();
{ Filter.lists.set(tpl.find('.js-list-filter input').value.trim());
'submit .js-list-filter'(evt) {
evt.preventDefault();
Filter.lists.set(this.find('.js-list-filter input').value.trim());
},
'change .js-field-card-filter'(evt) {
evt.preventDefault();
Filter.title.set(this.find('.js-field-card-filter').value.trim());
Filter.resetExceptions();
},
'click .js-toggle-label-filter'(evt) {
evt.preventDefault();
Filter.labelIds.toggle(this.currentData()._id);
Filter.resetExceptions();
},
'click .js-toggle-member-filter'(evt) {
evt.preventDefault();
Filter.members.toggle(this.currentData()._id);
Filter.resetExceptions();
},
'click .js-toggle-assignee-filter'(evt) {
evt.preventDefault();
Filter.assignees.toggle(this.currentData()._id);
Filter.resetExceptions();
},
'click .js-toggle-no-due-date-filter'(evt) {
evt.preventDefault();
Filter.dueAt.noDate();
Filter.resetExceptions();
},
'click .js-toggle-overdue-filter'(evt) {
evt.preventDefault();
Filter.dueAt.past();
Filter.resetExceptions();
},
'click .js-toggle-due-today-filter'(evt) {
evt.preventDefault();
Filter.dueAt.today();
Filter.resetExceptions();
},
'click .js-toggle-due-tomorrow-filter'(evt) {
evt.preventDefault();
Filter.dueAt.tomorrow();
Filter.resetExceptions();
},
'click .js-toggle-due-this-week-filter'(evt) {
evt.preventDefault();
Filter.dueAt.thisWeek();
Filter.resetExceptions();
},
'click .js-toggle-due-next-week-filter'(evt) {
evt.preventDefault();
Filter.dueAt.nextWeek();
Filter.resetExceptions();
},
'click .js-toggle-archive-filter'(evt) {
evt.preventDefault();
Filter.archive.toggle(this.currentData()._id);
Filter.resetExceptions();
const currentBoardId = Session.get('currentBoard');
if (!currentBoardId) return;
subManager.subscribe(
'board',
currentBoardId,
Filter.archive.isSelected(),
);
},
'click .js-toggle-hideEmpty-filter'(evt) {
evt.preventDefault();
Filter.hideEmpty.toggle(this.currentData()._id);
Filter.resetExceptions();
},
'click .js-toggle-custom-fields-filter'(evt) {
evt.preventDefault();
Filter.customFields.toggle(this.currentData()._id);
Filter.resetExceptions();
},
'change .js-field-advanced-filter'(evt) {
evt.preventDefault();
Filter.advanced.set(
this.find('.js-field-advanced-filter').value.trim(),
);
Filter.resetExceptions();
},
'click .js-clear-all'(evt) {
evt.preventDefault();
Filter.reset();
},
'click .js-filter-to-selection'(evt) {
evt.preventDefault();
const selectedCards = ReactiveCache.getCards(Filter.mongoSelector()).map(c => {
return c._id;
});
MultiSelection.add(selectedCards);
},
},
];
}, },
}).register('filterSidebar'); 'change .js-field-card-filter'(evt, tpl) {
evt.preventDefault();
Filter.title.set(tpl.find('.js-field-card-filter').value.trim());
Filter.resetExceptions();
},
'click .js-toggle-label-filter'(evt) {
evt.preventDefault();
Filter.labelIds.toggle(Template.currentData()._id);
Filter.resetExceptions();
},
'click .js-toggle-member-filter'(evt) {
evt.preventDefault();
Filter.members.toggle(Template.currentData()._id);
Filter.resetExceptions();
},
'click .js-toggle-assignee-filter'(evt) {
evt.preventDefault();
Filter.assignees.toggle(Template.currentData()._id);
Filter.resetExceptions();
},
'click .js-toggle-no-due-date-filter'(evt) {
evt.preventDefault();
Filter.dueAt.noDate();
Filter.resetExceptions();
},
'click .js-toggle-overdue-filter'(evt) {
evt.preventDefault();
Filter.dueAt.past();
Filter.resetExceptions();
},
'click .js-toggle-due-today-filter'(evt) {
evt.preventDefault();
Filter.dueAt.today();
Filter.resetExceptions();
},
'click .js-toggle-due-tomorrow-filter'(evt) {
evt.preventDefault();
Filter.dueAt.tomorrow();
Filter.resetExceptions();
},
'click .js-toggle-due-this-week-filter'(evt) {
evt.preventDefault();
Filter.dueAt.thisWeek();
Filter.resetExceptions();
},
'click .js-toggle-due-next-week-filter'(evt) {
evt.preventDefault();
Filter.dueAt.nextWeek();
Filter.resetExceptions();
},
'click .js-toggle-archive-filter'(evt) {
evt.preventDefault();
Filter.archive.toggle(Template.currentData()._id);
Filter.resetExceptions();
const currentBoardId = Session.get('currentBoard');
if (!currentBoardId) return;
subManager.subscribe(
'board',
currentBoardId,
Filter.archive.isSelected(),
);
},
'click .js-toggle-hideEmpty-filter'(evt) {
evt.preventDefault();
Filter.hideEmpty.toggle(Template.currentData()._id);
Filter.resetExceptions();
},
'click .js-toggle-custom-fields-filter'(evt) {
evt.preventDefault();
Filter.customFields.toggle(Template.currentData()._id);
Filter.resetExceptions();
},
'change .js-field-advanced-filter'(evt, tpl) {
evt.preventDefault();
Filter.advanced.set(
tpl.find('.js-field-advanced-filter').value.trim(),
);
Filter.resetExceptions();
},
'click .js-clear-all'(evt) {
evt.preventDefault();
Filter.reset();
},
'click .js-filter-to-selection'(evt) {
evt.preventDefault();
const selectedCards = ReactiveCache.getCards(Filter.mongoSelector()).map(c => {
return c._id;
});
MultiSelection.add(selectedCards);
},
});
async function mutateSelectedCards(mutationNameOrCallback, ...args) { async function mutateSelectedCards(mutationNameOrCallback, ...args) {
const cards = ReactiveCache.getCards(MultiSelection.getMongoSelector(), {sort: ['sort']}); const cards = ReactiveCache.getCards(MultiSelection.getMongoSelector(), {sort: ['sort']});
@ -181,67 +175,12 @@ function buildInsertionSortIndexes(cardsCount, targetCard, position, listId, swi
return indexes; return indexes;
} }
BlazeComponent.extendComponent({ function mapSelection(kind, _id) {
mapSelection(kind, _id) { return ReactiveCache.getCards(MultiSelection.getMongoSelector(), {sort: ['sort']}).map(card => {
return ReactiveCache.getCards(MultiSelection.getMongoSelector(), {sort: ['sort']}).map(card => { const methodName = kind === 'label' ? 'hasLabel' : 'isAssigned';
const methodName = kind === 'label' ? 'hasLabel' : 'isAssigned'; return card[methodName](_id);
return card[methodName](_id); });
}); }
},
allSelectedElementHave(kind, _id) {
if (MultiSelection.isEmpty()) return false;
else return _.every(this.mapSelection(kind, _id));
},
someSelectedElementHave(kind, _id) {
if (MultiSelection.isEmpty()) return false;
else return _.some(this.mapSelection(kind, _id));
},
events() {
return [
{
'click .js-toggle-label-multiselection'(evt) {
const labelId = this.currentData()._id;
const mappedSelection = this.mapSelection('label', labelId);
if (_.every(mappedSelection)) {
mutateSelectedCards('removeLabel', labelId);
} else if (_.every(mappedSelection, bool => !bool)) {
mutateSelectedCards('addLabel', labelId);
} else {
const popup = Popup.open('disambiguateMultiLabel');
// XXX We need to have a better integration between the popup and the
// UI components systems.
popup.call(this.currentData(), evt);
}
},
'click .js-toggle-member-multiselection'(evt) {
const memberId = this.currentData()._id;
const mappedSelection = this.mapSelection('member', memberId);
if (_.every(mappedSelection)) {
mutateSelectedCards('unassignMember', memberId);
} else if (_.every(mappedSelection, bool => !bool)) {
mutateSelectedCards('assignMember', memberId);
} else {
const popup = Popup.open('disambiguateMultiMember');
// XXX We need to have a better integration between the popup and the
// UI components systems.
popup.call(this.currentData(), evt);
}
},
'click .js-move-selection': Popup.open('moveSelection'),
'click .js-copy-selection': Popup.open('copySelection'),
'click .js-selection-color': Popup.open('setSelectionColor'),
'click .js-archive-selection'() {
mutateSelectedCards('archive');
EscapeActions.executeUpTo('multiselection');
},
},
];
},
}).register('multiselectionSidebar');
Template.multiselectionSidebar.helpers({ Template.multiselectionSidebar.helpers({
isBoardAdmin() { isBoardAdmin() {
@ -250,6 +189,53 @@ Template.multiselectionSidebar.helpers({
isCommentOnly() { isCommentOnly() {
return ReactiveCache.getCurrentUser().isCommentOnly(); return ReactiveCache.getCurrentUser().isCommentOnly();
}, },
allSelectedElementHave(kind, _id) {
if (MultiSelection.isEmpty()) return false;
else return _.every(mapSelection(kind, _id));
},
someSelectedElementHave(kind, _id) {
if (MultiSelection.isEmpty()) return false;
else return _.some(mapSelection(kind, _id));
},
});
Template.multiselectionSidebar.events({
'click .js-toggle-label-multiselection'(evt) {
const labelId = Template.currentData()._id;
const mappedSelection = mapSelection('label', labelId);
if (_.every(mappedSelection)) {
mutateSelectedCards('removeLabel', labelId);
} else if (_.every(mappedSelection, bool => !bool)) {
mutateSelectedCards('addLabel', labelId);
} else {
const popup = Popup.open('disambiguateMultiLabel');
// XXX We need to have a better integration between the popup and the
// UI components systems.
popup.call(Template.currentData(), evt);
}
},
'click .js-toggle-member-multiselection'(evt) {
const memberId = Template.currentData()._id;
const mappedSelection = mapSelection('member', memberId);
if (_.every(mappedSelection)) {
mutateSelectedCards('unassignMember', memberId);
} else if (_.every(mappedSelection, bool => !bool)) {
mutateSelectedCards('assignMember', memberId);
} else {
const popup = Popup.open('disambiguateMultiMember');
// XXX We need to have a better integration between the popup and the
// UI components systems.
popup.call(Template.currentData(), evt);
}
},
'click .js-move-selection': Popup.open('moveSelection'),
'click .js-copy-selection': Popup.open('copySelection'),
'click .js-selection-color': Popup.open('setSelectionColor'),
'click .js-archive-selection'() {
mutateSelectedCards('archive');
EscapeActions.executeUpTo('multiselection');
},
}); });
Template.disambiguateMultiLabelPopup.events({ Template.disambiguateMultiLabelPopup.events({

View file

@ -1,41 +1,31 @@
BlazeComponent.extendComponent({ Template.searchSidebar.onCreated(function () {
onCreated() { this.term = new ReactiveVar('');
this.term = new ReactiveVar(''); });
},
Template.searchSidebar.helpers({
cards() { cards() {
const currentBoard = Utils.getCurrentBoard(); const currentBoard = Utils.getCurrentBoard();
return currentBoard.searchCards(this.term.get()); return currentBoard.searchCards(Template.instance().term.get());
}, },
lists() { lists() {
const currentBoard = Utils.getCurrentBoard(); const currentBoard = Utils.getCurrentBoard();
return currentBoard.searchLists(this.term.get()); return currentBoard.searchLists(Template.instance().term.get());
}, },
});
clickOnMiniCard(evt) { Template.searchSidebar.events({
'click .js-minicard'(evt) {
if (Utils.isMiniScreen()) { if (Utils.isMiniScreen()) {
evt.preventDefault(); evt.preventDefault();
Session.set('popupCardId', this.currentData()._id); Session.set('popupCardId', Template.currentData()._id);
this.cardDetailsPopup(evt); if (!Popup.isOpen()) {
Popup.open("cardDetails")(evt);
}
} }
}, },
'submit .js-search-term-form'(evt, tpl) {
cardDetailsPopup(event) { evt.preventDefault();
if (!Popup.isOpen()) { tpl.term.set(evt.target.searchTerm.value);
Popup.open("cardDetails")(event);
}
}, },
});
events() {
return [
{
'click .js-minicard': this.clickOnMiniCard,
'submit .js-search-term-form'(evt) {
evt.preventDefault();
this.term.set(evt.target.searchTerm.value);
},
},
];
},
}).register('searchSidebar');