Appreciation Editor Complete / All Others Incomplete

The text editor for the appreciation page is complete, but all other files are empty, besides User Manager.
This commit is contained in:
2018-08-17 11:24:00 -07:00
parent 26728082dc
commit 2593d0f977
24 changed files with 502 additions and 388 deletions

View File

@@ -1,4 +1,7 @@
<template name="AppreciationEditor">
<div id="appreciationEditor">
<h1>Appreciation Editor</h1>
<div id="editor"></div>
<button id="save">Save</button>
</div>
</template>

View File

@@ -0,0 +1,6 @@
#appreciationEditor
display: block
.ck.ck-editor__editable_inline
border-color: rgba(0,0,0,.2)
.ck.ck-editor__editable_inline.ck-focused
border-color: rgba(0,0,0,1)

View File

@@ -1,2 +1,77 @@
import CKEditor from '@ckeditor/ckeditor5-build-balloon';
import './AppreciationEditor.html';
import swal from "sweetalert2";
import './AppreciationEditor.html';
let originalData = "";
Tracker.autorun(function() {
Meteor.subscribe("pages");
});
Template.AppreciationEditor.onRendered(function() {
let _this = this;
//#appreciationEditor'
CKEditor.create(document.querySelector('#editor'), {}).then(editor => {
_this.ckEditor = editor;
Tracker.autorun(function() {
let doc = Meteor.collections.Pages.findOne({name: 'Appreciation'});
originalData = (doc === undefined ? "" : doc.html);
editor.setData(originalData);
});
}).catch(err => {
console.error(err);
});
});
Template.AppreciationEditor.onDestroyed(function() {
let data = this.ckEditor.getData();
if(data != originalData) {
swal({
title: "Save Changes",
text: "Would you like to save any changes you have made to this sheet?",
type: "question",
showCancelButton: true,
confirmButtonColor: "#7cdd7f",
confirmButtonText: "Yes",
cancelButtonText: "No"
}).then(
function(isConfirm) {
if(isConfirm) {
Meteor.call('updatePage', 'Appreciation', data, function (error, result) {
if (error) sAlert.error(error);
else sAlert.success("Content Saved Successfully");
});
}
},
function(dismiss) {}
);
}
});
Template.AppreciationEditor.helpers({
// html: function() {
// let doc = Meteor.collections.Pages.findOne({name: 'Appreciation'});
//
// return doc == undefined ? "" : doc.html;
// }
});
Template.AppreciationEditor.events({
'click #save': function(event, template) {
let data = template.ckEditor.getData();
if(data != originalData) {
Meteor.call('updatePage', 'Appreciation', data, function (error, result) {
if (error) sAlert.error(error);
else sAlert.success("Content Saved Successfully");
});
}
else {
sAlert.success("Data has not changed!");
}
}
});

View File

@@ -1,2 +0,0 @@
#appreciationEditor
display: block

View File

@@ -0,0 +1,78 @@
<template name="UserManagement">
<div id="userManagement">
{{#if Template.subscriptionsReady}}
<div class="tableControls">
<div class="contentControls">
<a class="loadMoreLink {{#if disableLoadMore}}disabled{{/if}}" href="javascript:">Load More...</a>
</div>
</div>
<div class="separatedTableHeader">
<table class="table table-striped table-hover">
<thead>
<tr>
<th class="username">Username {{>UserSearch columnName='username'}}</th>
<th class="email">Email {{>UserSearch columnName='email' collectionQueryColumnName='name' collection='Items' collectionResultColumnName='_id'}}</th>
<th class="roles">Roles</th>
<th class="actions">Actions <span class="newUserButton btn btn-success"><i class="fa fa-plus-circle" aria-hidden="true"></i><i class="fa fa-times-circle" aria-hidden="true"></i></span></th>
</tr>
</thead>
</table>
</div>
<div class="listContianer">
<div class="listRow">
<div class="listCell">
<div class="tableContainer mCustomScrollbar" data-mcs-theme="dark">
<table class="dataTable table table-striped table-hover">
<tbody>
{{#if displayNewUser}}
<tr>{{> UserEditor isNew=true}}</tr>
{{/if}}
{{#each users}}
{{> User}}
{{/each}}
</tbody>
</table>
</div>
</div>
</div>
</div>
{{else}}
{{/if}}
</div>
</template>
<template name="User">
<tr>
{{#if editing}}
{{> UserEditor}}
{{else}}
<td class="username tdLarge noselect nonclickable">{{username}}</td>
<td class="email tdLarge noselect nonclickable">{{email}}</td>
<td class="roles tdLarge noselect nonclickable">{{roles}}</td>
<td class="actions center tdLarge"><i class="userRemove fa fa-times-circle fa-lg noselect clickable" aria-hidden="true"></i>&nbsp;/&nbsp;<i class="userEdit fa fa-pencil-square-o fa-lg noselect clickable" aria-hidden="true"></i></td>
{{/if}}
</tr>
</template>
<template name="UserEditor">
<td colspan="3" class="userEditor measureEditorTd">
<div>
<div class="username editorDiv"><label>User Name:</label><input name="username" class="form-control" type="text" value="{{username}}" autocomplete="off" required></div>
<div class="email editorDiv"><label>User Email:</label><input name="email" class="form-control" type="text" value="{{email}}" autocomplete="off" required></div>
<div class="rolesContainer editorDiv"><label>Roles:</label>
<div class="roles center" style="font-size: 1.2em">
{{#each allRoles}}
<span class="role {{getRoleState this}} noselect">{{name}}</span>
{{/each}}
</div>
</div>
</div>
</td>
<td class="actions center measureEditorTd"><i class="editorApply fa fa-check-square-o fa-lg noselect clickable" aria-hidden="true"></i>&nbsp;/&nbsp;<i class="editorCancel fa fa-times-circle fa-lg noselect clickable" aria-hidden="true"></i></td>
</template>
<template name="UserSearch">
<div class="">
<input type="text" class="searchInput" placeholder="Filter..." value="{{searchValue}}" style="width: 90%"/>
</div>
</template>

View File

@@ -0,0 +1,152 @@
#userManagement
display: table
content-box: border-box
padding: 10px 20px
height: 100%
width: 100%
text-align: left
position: relative
.tableControls
display: table
width: 100%
text-align: right
margin-right: 20px
.contentControls
vertical-align: bottom
display: table-cell
text-align: right
min-width: 100px
a
font-size: 12px
font-family: "Arial", san-serif
font-weight: 800
color: #2d1b8c
text-decoration: none
a:hover
text-decoration: underline
a.disabled
visibility: hidden
.editor
height: 100%
overflow-y: auto
.insert
flex: none
width: 100%
.col-md-6
padding: 10px 30px 0 30px
background: #EFEFEF
border-radius: 1em
.formGroupHeading
font-size: 1.6em
font-family: "Arial Black", "Arial Bold", Gadget, sans-serif
font-style: normal
font-variant: normal
font-weight: 500
.table
table-layout: fixed
min-width: 100%
thead, tbody
> tr
> .username
width: 50%
min-width: 100px
> .email
width: 50%
min-width: 100px
> .roles
width: 260px
min-width: 260px
> .actions
width: 80px
min-width: 80px
.separatedTableHeader
.actions
text-align: center
.newUserButton
margin-top: 4px
padding: 0 12px
.fa-plus-circle
display: inline-block
.fa-times-circle
display: none
.newUserButton.active
background-color: #fb557b
color: black
.fa-times-circle
display: inline-block
.fa-plus-circle
display: none
.listContainer
position: absolute
top: 100px
left: 0
right: 0
bottom: 0
.listRow
display: table-row
.listCell
display: table-cell
position: relative
height: 100%
width: 100%
.tableContainer
position: absolute
top: 0
bottom: 0
left: 0
right: 0
width: auto
height: auto
border: 0
font-size: 12.5px
overflow-y: auto
table
table-layout: fixed
width: 100%
thead
display: none
visibility: hidden
.userRemove
color: red
.userEdit
color: darkblue
.editorApply
color: green
.editorCancel
color: red
.userEditor > div
display: table
> div
display: table-cell
padding: 10px
.roles
.role
vertical-align: middle
td.roles
.role
padding: 4px 4px
border: 1px solid #555
border-radius: .25em
background: white
color: #999
cursor: pointer
.selected
color: black
div.roles
padding: 4px 0
.role
padding: 4px 4px
border: 1px solid #555
border-radius: .25em
background: white
color: #999
cursor: pointer
.selected
color: black
.center
vertical-align: middle !important

View File

@@ -0,0 +1,223 @@
import './UserManagement.html';
import '/imports/util/selectize/selectize.js'
let QUERY_LIMIT = 100;
let QUERY_LIMIT_INCREMENT = 100;
let PREFIX = "UserManagement";
Tracker.autorun(function() {
Meteor.subscribe("users", Session.get(PREFIX + 'searchQuery'));
Meteor.subscribe("roles");
});
Template.UserManagement.onCreated(function() {
Session.set(PREFIX + "displayNewUser", false);
Session.set(PREFIX + "queryLimit", QUERY_LIMIT);
});
Template.UserManagement.onRendered(function() {
$(".tableContainer").mCustomScrollbar({
scrollButtons: {enable:true},
theme: "light-thick",
scrollbarPosition: "outside",
scrollEasing: "linear"
});
});
Template.UserManagement.helpers({
displayNewUser: function() {
return Session.get(PREFIX + "displayNewUser");
},
users: function() {
let skipCount = Session.get(PREFIX + 'skipCount') || 0;
let query = Session.get(PREFIX + 'searchQuery');
let dbQuery = [];
if(query) {
_.each(_.keys(query), function(key) {
if(_.isFunction(query[key])) dbQuery.push({[key]: query[key]}); //dbQuery[key] = query[key]();
else if(_.isObject(query[key])) dbQuery.push({[key]: query[key]}); //dbQuery[key] = query[key]; //Will look something like: {$in: [xxx,xxx,xxx]}
else if(_.isNumber(query[key])) dbQuery.push({[key]: query[key]}); //dbQuery[key] = query[key];
else {
//dbQuery[key] = {$regex: query[key], $options: 'i'};
let searchValue = query[key];
let searches = searchValue && searchValue.length > 0 ? searchValue.split(/\s+/) : undefined;
for(let search of searches) {
dbQuery.push({[key]: {$regex: '\\b' + search, $options: 'i'}});
}
}
})
}
if(!Session.get(PREFIX + "showHidden")) {
//Ignore any hidden elements by showing those not hidden, or those without the hidden field.
dbQuery.push({$or: [{hidden: false}, {hidden: {$exists:false}}]});
}
dbQuery = dbQuery.length > 0 ? {$and: dbQuery} : {};
Session.set(PREFIX + 'userCount', Meteor.collections.Users.find(dbQuery).count()); //Always get a full count.
return Meteor.collections.Users.find(dbQuery, {limit: Session.get(PREFIX + "queryLimit"), skip: skipCount, sort: {username: 1}});
},
disableLoadMore: function() {
return Session.get(PREFIX + 'userCount') - (Session.get(PREFIX + 'skipCount') || 0) - Session.get(PREFIX + "queryLimit") <= 0;
}
});
Template.UserManagement.events({
'click .loadMoreLink': function(event, template) {
event.preventDefault();
Session.set(PREFIX + 'queryLimit', Session.get(PREFIX + "queryLimit") + QUERY_LIMIT_INCREMENT);
},
'click .newUserButton': function(event, template) {
if(template.$('.newUserButton').hasClass('active')) {
Session.set(PREFIX + 'displayNewUser', false);
}
else {
Session.set(PREFIX + 'displayNewUser', true);
Session.set(PREFIX + "editedUser", undefined); //Clear the edited user so that only one editor is open at a time.
}
template.$('.newUserButton').toggleClass('active');
}
});
Template.User.onCreated(function() {
this.edited = new ReactiveVar();
});
Template.User.events({
"click .userEdit": function(event, template) {
//template.edited.set(this);
Session.set(PREFIX + "editedUser", this._id);
Session.set(PREFIX + 'displayNewUser', false); //Ensure the new measure editor is closed.
template.parentTemplate().$('.newUserButton').removeClass('active');
},
"click .userRemove": function(event, template) {
let _this = this;
bootbox.confirm({
message: "Delete the user?",
buttons: {confirm: {label: "Yes", className: 'btn-success'}, cancel: {label: "No", className: "btn-danger"}},
callback: function(result) {
if(result) {
Meteor.call('deleteUser', _this._id, function(error, result) {
if(error) {
sAlert.error(error);
}
else {
sAlert.success("User removed.");
}
});
}
}
});
}
});
Template.User.helpers({
email: function() {
return this.emails && this.emails.length > 0 ? this.emails[0].address : "";
},
editing: function() {
let editedUser = Session.get(PREFIX + "editedUser");
return editedUser == this._id;
}
});
Template.UserEditor.helpers({
email: function() {
return this.emails && this.emails.length > 0 ? this.emails[0].address : "";
},
allRoles: function() {
return Meteor.collections.UserRoles.find();
},
getRoleState: function(role) {
let user = Template.parentData(1);
return !user.isNew && user.roles.includes(role.name) ? "selected" : "";
}
});
Template.UserEditor.events({
"click .editorCancel": function(event, template) {
Session.set(PREFIX + "editedUser", undefined);
Session.set(PREFIX + 'displayNewUser', false);
template.parentTemplate().$('.newUserButton').removeClass('active');
},
"click .editorApply": function(event, template) {
let user = {};
let roles = [];
user.username = template.$('input[name="username"]').val();
user.email = template.$('input[name="email"]').val();
let roleSpans = template.$('.role.selected');
for(let i = 0; i < roleSpans.length; i++) {
roles.push($(roleSpans[i]).text());
}
user.roles = roles;
if(Session.get(PREFIX + 'displayNewUser')) {
Meteor.call('insertUser', user, function(error, result) {
if(error) {
sAlert.error(error);
}
else {
sAlert.success("User created.");
Session.set(PREFIX + 'displayNewUser', false);
template.parentTemplate().$('.newUserButton').removeClass('active');
}
});
}
else {
user._id = this._id;
Meteor.call("updateUser", user, function(error, result) {
if(error) sAlert.error(error);
else {
sAlert.success("User updated.");
Session.set(PREFIX + "editedUser", undefined);
template.parentTemplate().$('.newUserButton').removeClass('active');
}
});
}
},
"click .role": function(event, template) {
$(event.target).toggleClass("selected");
}
});
Template.UserSearch.events({
"keyup .searchInput": _.throttle(function(event, template) {
let searchQuery = Session.get(PREFIX + 'searchQuery') || {};
let searchFields = Session.get(PREFIX + 'searchFields') || {};
let searchValue = template.$('.searchInput').val();
if(searchValue) {
if(this.number) searchValue = parseFloat(searchValue);
if(this.collection) {
let ids = Meteor.collections[this.collection].find({[this.collectionQueryColumnName]: {$regex: searchValue, $options: 'i'}}, {fields: {[this.collectionResultColumnName]: 1}}).fetch();
//Convert the ids to an array of ids instead of an array of objects containing an id.
for(let i = 0; i < ids.length; i++) {ids[i] = ids[i]._id;}
searchQuery[this.columnName] = {$in: ids};
searchFields[this.columnName] = searchValue;
}
else {
searchFields[this.columnName] = searchQuery[this.columnName] = searchValue;
}
}
else {
//Remove columns from the search query whose values are empty so we don't bother the database with them.
delete searchQuery[this.columnName];
delete searchFields[this.columnName];
}
Session.set(PREFIX + 'searchQuery', searchQuery);
Session.set(PREFIX + 'skipCount', 0); //Reset the paging of the results.
}, 500)
});
Template.UserSearch.helpers({
searchValue: function() {
let searchFields = Session.get(PREFIX + 'searchFields');
return (searchFields && searchFields[this.columnName]) ? searchFields[this.columnName] : '';
}
});