Upgraded to Meteor 1.6.0.1 and NodeJS 8.9.3. Added weekly/daily property to venues to support graphing and tracking of actual income from farmers markets (they don't usually match with expected income). Added workers objects to help illustrate who did what work (who was at the market on a specific week for example, or who prep'd and who canned a batch of jam). Fixed some bugs in the venue page. Re-design of the menu to allow for more menu options.
This commit is contained in:
@@ -71,7 +71,7 @@
|
||||
<div class="editorDiv"><label>Name:</label><input name="name" class="form-control" type="text" value="{{name}}" autocomplete="off" required></div>
|
||||
<div class="editorDiv"><label>Postfix:</label><input name="postfix" class="form-control" type="text" value="{{name}}" autocomplete="off" required></div>
|
||||
</td>
|
||||
<td class="center measureEditorTd"><i class="editorApply fa fa-check-square-o fa-lg noselect clickable" aria-hidden="true"></i> / <i class="editorCancel fa fa-times-circle fa-lg noselect clickable" aria-hidden="true"></i></td>
|
||||
<td class="center measureEditorTd actions"><i class="editorApply fa fa-check-square-o fa-lg noselect clickable" aria-hidden="true"></i> / <i class="editorCancel fa fa-times-circle fa-lg noselect clickable" aria-hidden="true"></i></td>
|
||||
</template>
|
||||
|
||||
<template name="MeasureSearch">
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
<tr>
|
||||
<th class="name">Name {{>VenueSearch columnName='name'}}</th>
|
||||
<th class="type">Type {{>VenueSearch columnName='type'}}</th>
|
||||
<th class="type">Frequency</th>
|
||||
<th class="actions">Actions <span class="newVenueButton 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>
|
||||
@@ -31,7 +32,7 @@
|
||||
<table class="table table-striped table-hover">
|
||||
<tbody>
|
||||
{{#if displayNewVenue}}
|
||||
{{> VenueEditor isNew=true}}
|
||||
<tr>{{> VenueEditor isNew=true}}</tr>
|
||||
{{/if}}
|
||||
{{#each venues}}
|
||||
{{> Venue}}
|
||||
@@ -53,6 +54,7 @@
|
||||
{{else}}
|
||||
<td class="name noselect nonclickable left">{{name}}</td>
|
||||
<td class="type noselect nonclickable left">{{type}}</td>
|
||||
<td class="type noselect nonclickable left">{{frequency}}</td>
|
||||
{{#if hidden}}
|
||||
<td class="actions center"><i class="actionEdit fa fa-pencil-square-o fa-lg noselect clickable" title="Edit" aria-hidden="true"></i> / <i class="actionShow fa fa-eye fa-lg noselect clickable" title="Show" aria-hidden="true"></i></td>
|
||||
{{else}}
|
||||
@@ -67,11 +69,12 @@
|
||||
</template>
|
||||
|
||||
<template name="VenueEditor">
|
||||
<td colspan="2" class="venueEditorTd">
|
||||
<div class="editorDiv"><label>Name:</label><input name="name" class="form-control" type="text" value="{{name}}" autocomplete="off" required></div>
|
||||
<div class="editorDiv"><label>Type:</label><input name="type" class="form-control" type="text" value="{{name}}" autocomplete="off" required></div>
|
||||
<td colspan="3" class="venueEditorTd left">
|
||||
<div class="editorDiv"><label>Name</label> <input name="name" class="form-control" type="text" value="{{name}}" autocomplete="off" required></div>
|
||||
<div class="editorDiv"><label>Type</label> <input name="type" class="form-control" type="text" autocomplete="off" required></div>
|
||||
<div class="editorDiv"><label>Frequency</label> <input name="frequency" class="form-control" type="text" autocomplete="off" required></div>
|
||||
</td>
|
||||
<td class="center venueEditorTd"><i class="editorApply fa fa-check-square-o fa-lg noselect clickable" aria-hidden="true"></i> / <i class="editorCancel fa fa-times-circle fa-lg noselect clickable" aria-hidden="true"></i></td>
|
||||
<td class="venueEditorTd actions center"><i class="editorApply fa fa-check-square-o fa-lg noselect clickable" aria-hidden="true"></i> / <i class="editorCancel fa fa-times-circle fa-lg noselect clickable" aria-hidden="true"></i></td>
|
||||
</template>
|
||||
|
||||
<template name="VenueSearch">
|
||||
|
||||
7
imports/ui/Venues.import.styl
vendored
7
imports/ui/Venues.import.styl
vendored
@@ -46,10 +46,13 @@
|
||||
thead, tbody
|
||||
> tr
|
||||
> .name
|
||||
width: 50%
|
||||
width: 33%
|
||||
min-width: 100px
|
||||
> .type
|
||||
width: 50%
|
||||
width: 33%
|
||||
min-width: 100px
|
||||
> .frequency
|
||||
width: 33%
|
||||
min-width: 100px
|
||||
> .actions
|
||||
width: 90px
|
||||
|
||||
@@ -181,7 +181,16 @@ Template.Venue.events({
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
Template.VenueEditor.onCreated(function() {
|
||||
this.selectedType = new ReactiveVar(this.data.type);
|
||||
this.selectedFrequency = new ReactiveVar(this.data.frequency);
|
||||
});
|
||||
Template.VenueEditor.onRendered(function() {
|
||||
let schema = Meteor.collections.Venues.simpleSchema();
|
||||
|
||||
this.$('[name="type"]').buildCombo({set: schema.constants.types, selection: this.selectedType, comparator: function(a, b) {return a === b;}, textAttr: undefined, listClass: 'comboList'});
|
||||
this.$('[name="frequency"]').buildCombo({set: schema.constants.frequencies, selection: this.selectedFrequency, comparator: function(a, b) {return a === b;}, textAttr: undefined, listClass: 'comboList'});
|
||||
});
|
||||
Template.VenueEditor.helpers({
|
||||
});
|
||||
Template.VenueEditor.events({
|
||||
@@ -193,10 +202,11 @@ Template.VenueEditor.events({
|
||||
"click .editorApply": function(event, template) {
|
||||
let name = template.$("input[name='name']").val().trim();
|
||||
let type = template.$("input[name='type']").val().trim();
|
||||
let frequency = template.$("input[name='frequency']").val().trim();
|
||||
let order = 0; //TODO:
|
||||
|
||||
if(Session.get(PREFIX + 'displayNewVenue')) {
|
||||
Meteor.call("createVenue", name, type, order, function(error, result) {
|
||||
Meteor.call("createVenue", name, type, frequency, order, function(error, result) {
|
||||
if(error) sAlert.error(error);
|
||||
else {
|
||||
sAlert.success("Venue created.");
|
||||
@@ -206,7 +216,7 @@ Template.VenueEditor.events({
|
||||
});
|
||||
}
|
||||
else {
|
||||
Meteor.call("updateVenue", this._id, name, type, order, function(error, result) {
|
||||
Meteor.call("updateVenue", this._id, name, type, frequency, order, function(error, result) {
|
||||
if(error) sAlert.error(error);
|
||||
else {
|
||||
sAlert.success("Venue updated.");
|
||||
|
||||
90
imports/ui/Workers.html
Normal file
90
imports/ui/Workers.html
Normal file
@@ -0,0 +1,90 @@
|
||||
<template name="Workers">
|
||||
<div id="workers">
|
||||
{{#if Template.subscriptionsReady}}
|
||||
<div class="tableControls">
|
||||
<div class="showHidden">
|
||||
<span class="controlLabel">Show Hidden</span>
|
||||
<div class="toggleShowHidden checkbox checkbox-slider--b-flat">
|
||||
<label>
|
||||
<input type="checkbox" name="showHidden"><span></span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<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="name">Name {{>WorkerSearch columnName='name'}}</th>
|
||||
<th class="activities">Activities</th>
|
||||
<th class="hourlyRate">Avg Hourly Rate</th>
|
||||
<th class="actions">Actions <span class="newWorkerButton 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="listRow">
|
||||
<div class="listCell">
|
||||
<div class="tableContainer mCustomScrollbar" data-mcs-theme="dark">
|
||||
<table class="table table-striped table-hover">
|
||||
<tbody>
|
||||
{{#if displayNewWorker}}
|
||||
<tr>{{> WorkerEditor isNew=true}}</tr>
|
||||
{{/if}}
|
||||
{{#each workers}}
|
||||
{{> Worker}}
|
||||
{{/each}}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{{else}}
|
||||
{{/if}}
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<template name="Worker">
|
||||
<tr class="{{getRowClass}}">
|
||||
{{#if editing}}
|
||||
{{> WorkerEditor}}
|
||||
{{else}}
|
||||
<td class="name noselect nonclickable left">{{name}}</td>
|
||||
<td class="activities noselect nonclickable left">{{activities}}</td>
|
||||
<td class="hourlyRate noselect nonclickable left">{{hourlyRate}}</td>
|
||||
{{#if hidden}}
|
||||
<td class="actions center"><i class="actionEdit fa fa-pencil-square-o fa-lg noselect clickable" title="Edit" aria-hidden="true"></i> / <i class="actionShow fa fa-eye fa-lg noselect clickable" title="Show" aria-hidden="true"></i></td>
|
||||
{{else}}
|
||||
{{#if deactivated}}
|
||||
<td class="actions center"><i class="actionEdit fa fa-pencil-square-o fa-lg noselect clickable" title="Edit" aria-hidden="true"></i> / <i class="actionActivate fa fa-toggle-on fa-lg noselect clickable" title="Activate" aria-hidden="true"></i> / <i class="actionHide fa fa-eye-slash fa-lg noselect clickable" title="Hide" aria-hidden="true"></i></td>
|
||||
{{else}}
|
||||
<td class="actions center"><i class="actionEdit fa fa-pencil-square-o fa-lg noselect clickable" title="Edit" aria-hidden="true"></i> / <i class="actionRemove fa fa-times-circle fa-lg noselect clickable" title="Deactivate" aria-hidden="true"></i></td>
|
||||
{{/if}}
|
||||
{{/if}}
|
||||
{{/if}}
|
||||
</tr>
|
||||
</template>
|
||||
|
||||
<template name="WorkerEditor">
|
||||
<td colspan="3" class="workerEditorTd">
|
||||
<div class="editorDiv"><label>Name</label> <input name="name" class="form-control" type="text" value="{{name}}" autocomplete="off" required></div>
|
||||
<div class="editorDiv"><label>Activities</label>
|
||||
<select class="activitiesEditor" multiple="multiple">
|
||||
{{#each activities}}
|
||||
<option value="{{this}}" {{activitySelected}}>{{this}}</option>
|
||||
{{/each}}
|
||||
</select>
|
||||
</div>
|
||||
<div class="editorDiv"><label>Avg Hourly Rate</label> <input name="hourlyRate" class="form-control" type="number" min="0" step="0.01" data-schema-key='currency' value="{{name}}" autocomplete="off" required></div>
|
||||
</td>
|
||||
<td class="center workerEditorTd actions"><i class="editorApply fa fa-check-square-o fa-lg noselect clickable" aria-hidden="true"></i> / <i class="editorCancel fa fa-times-circle fa-lg noselect clickable" aria-hidden="true"></i></td>
|
||||
</template>
|
||||
|
||||
<template name="WorkerSearch">
|
||||
<div class="workerSearch">
|
||||
<input type="text" class="searchInput" placeholder="Filter..." value="{{searchValue}}"/>
|
||||
</div>
|
||||
</template>
|
||||
145
imports/ui/Workers.import.styl
vendored
Normal file
145
imports/ui/Workers.import.styl
vendored
Normal file
@@ -0,0 +1,145 @@
|
||||
#workers
|
||||
display: table
|
||||
content-box: border-box
|
||||
padding: 10px 20px
|
||||
height: 100%
|
||||
width: 100%
|
||||
text-align: left
|
||||
|
||||
.tableControls
|
||||
display: table
|
||||
width: 100%
|
||||
text-align: right
|
||||
margin-right: 20px
|
||||
.showHidden
|
||||
display: table-cell
|
||||
width: 100%
|
||||
.controlLabel
|
||||
font-size: 9px
|
||||
font-weight: 700
|
||||
color: #5a5a5a
|
||||
position: relative
|
||||
top: -2px
|
||||
.toggleShowHidden
|
||||
margin: 0 40px 0 0
|
||||
position: relative
|
||||
top: -4px
|
||||
display: inline-block
|
||||
.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
|
||||
.table
|
||||
table-layout: fixed
|
||||
min-width: 100%
|
||||
thead, tbody
|
||||
> tr
|
||||
> .name
|
||||
width: 30%
|
||||
min-width: 100px
|
||||
> .activities
|
||||
width: 50%
|
||||
min-width: 100px
|
||||
> .hourlyRate
|
||||
width: 20%
|
||||
min-width: 100px
|
||||
> .actions
|
||||
width: 90px
|
||||
min-width: 90px
|
||||
.separatedTableHeader
|
||||
table
|
||||
thead
|
||||
> tr
|
||||
.actions
|
||||
text-align: center
|
||||
.newWorkerButton
|
||||
margin-top: 4px
|
||||
padding: 0 12px
|
||||
.fa-plus-circle
|
||||
display: inline-block
|
||||
.fa-times-circle
|
||||
display: none
|
||||
.newWorkerButton.active
|
||||
background-color: #fb557b
|
||||
color: black
|
||||
.fa-times-circle
|
||||
display: inline-block
|
||||
.fa-plus-circle
|
||||
display: none
|
||||
.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%
|
||||
.workerSearch
|
||||
margin: 3px 0 2px 1px
|
||||
.workerEditorTd
|
||||
background: #deeac0
|
||||
input[name="name"], .activitiesEditor, input[name="hourlyRate"]
|
||||
width: 100%
|
||||
.editorDiv
|
||||
margin: 4px 0
|
||||
label
|
||||
font-family: "Arial Black", "Arial Bold", Gadget, sans-serif
|
||||
font-size: .9em
|
||||
padding-bottom: 4px
|
||||
select2
|
||||
font-size: .4em
|
||||
> thead
|
||||
display: none
|
||||
visibility: hidden
|
||||
> tbody
|
||||
> tr
|
||||
.actionRemove
|
||||
color: #F77
|
||||
.actionEdit
|
||||
color: #44F
|
||||
.editorApply
|
||||
color: green
|
||||
.editorCancel
|
||||
color: red
|
||||
> tr.deactivated
|
||||
background-color: #fac0d1
|
||||
.actionActivate
|
||||
color: #158b18
|
||||
.actionHide
|
||||
color: #6a0707
|
||||
.actionEdit
|
||||
color: #0101e4
|
||||
> tr.deactivated:hover
|
||||
background-color: #ffcadb
|
||||
> tr.hidden
|
||||
background-color: #e995ff
|
||||
.actionEdit
|
||||
color: #0101e4
|
||||
.actionShow
|
||||
color: #027905
|
||||
> tr.hidden:hover
|
||||
background-color: #ffb5ff
|
||||
237
imports/ui/Workers.js
Normal file
237
imports/ui/Workers.js
Normal file
@@ -0,0 +1,237 @@
|
||||
|
||||
import './Workers.html';
|
||||
|
||||
let QUERY_LIMIT = 100;
|
||||
let QUERY_LIMIT_INCREMENT = 100;
|
||||
let PREFIX = "Workers.";
|
||||
|
||||
Tracker.autorun(function() {
|
||||
Meteor.subscribe("Workers");
|
||||
});
|
||||
|
||||
Template.Workers.onCreated(function() {
|
||||
Session.set(PREFIX + "displayNewWorker", false);
|
||||
Session.set(PREFIX + "showHidden", false);
|
||||
Session.set(PREFIX + "queryLimit", QUERY_LIMIT);
|
||||
});
|
||||
Template.Workers.onRendered(function() {
|
||||
$(".tableContainer").mCustomScrollbar({
|
||||
scrollButtons: {enable:true},
|
||||
theme: "light-thick",
|
||||
scrollbarPosition: "outside",
|
||||
scrollEasing: "linear"
|
||||
});
|
||||
});
|
||||
Template.Workers.helpers({
|
||||
displayNewWorker: function() {
|
||||
return Session.get(PREFIX + "displayNewWorker");
|
||||
},
|
||||
workers: 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 + 'workerCount', Meteor.collections.Workers.find(dbQuery).count()); //Always get a full count.
|
||||
return Meteor.collections.Workers.find(dbQuery, {limit: Session.get(PREFIX + "queryLimit"), skip: skipCount, sort: {order: 1}});
|
||||
},
|
||||
disableLoadMore: function() {
|
||||
return Session.get(PREFIX + 'workerCount') - (Session.get(PREFIX + 'skipCount') || 0) - Session.get(PREFIX + "queryLimit") <= 0;
|
||||
}
|
||||
});
|
||||
Template.Workers.events({
|
||||
'click .loadMoreLink': function(event, template) {
|
||||
event.preventDefault();
|
||||
Session.set(PREFIX + 'queryLimit', Session.get(PREFIX + "queryLimit") + QUERY_LIMIT_INCREMENT);
|
||||
},
|
||||
'click .newWorkerButton': function(event, template) {
|
||||
if(template.$('.newWorkerButton').hasClass('active')) {
|
||||
Session.set(PREFIX + 'displayNewWorker', false);
|
||||
}
|
||||
else {
|
||||
Session.set(PREFIX + 'displayNewWorker', true);
|
||||
Session.set(PREFIX + "editedWorker", undefined); //Clear the edited worker so that only one editor is open at a time.
|
||||
}
|
||||
template.$('.newWorkerButton').toggleClass('active');
|
||||
},
|
||||
'change input[name="showHidden"]': function(event, template) {
|
||||
Session.set(PREFIX + "showHidden", $(event.target).prop('checked'));
|
||||
}
|
||||
});
|
||||
|
||||
Template.WorkerSearch.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 + 'searchFields', searchFields);
|
||||
Session.set(PREFIX + 'skipCount', 0); //Reset the paging of the results.
|
||||
}, 500)
|
||||
});
|
||||
Template.WorkerSearch.helpers({
|
||||
searchValue: function() {
|
||||
let searchFields = Session.get(PREFIX + 'searchFields');
|
||||
|
||||
return (searchFields && searchFields[this.columnName]) ? searchFields[this.columnName] : '';
|
||||
}
|
||||
});
|
||||
|
||||
Template.Worker.helpers({
|
||||
workers: function() {
|
||||
let result = "";
|
||||
|
||||
if(this.workers && this.workers.length > 0) {
|
||||
let workerNames = [];
|
||||
|
||||
for(let i = 0; i < this.workers.length; i++) {
|
||||
let workerObject = Meteor.collections.Workers.findOne(this.workers[i]);
|
||||
|
||||
if(workerObject && workerObject.name)
|
||||
workerNames.push(workerObject.name);
|
||||
}
|
||||
|
||||
result = workerNames.join(", ");
|
||||
}
|
||||
|
||||
return result;
|
||||
},
|
||||
editing: function() {
|
||||
let editedWorker = Session.get(PREFIX + "editedWorker");
|
||||
|
||||
return editedWorker == this._id;
|
||||
},
|
||||
getRowClass: function() {
|
||||
return this.hidden ? "hidden" : this.deactivated ? "deactivated" : "";
|
||||
}
|
||||
});
|
||||
Template.Worker.events({
|
||||
"click .actionEdit": function(event, template) {
|
||||
Session.set(PREFIX + "editedWorker", this._id);
|
||||
Session.set(PREFIX + 'displayNewWorker', false); //Ensure the new worker editor is closed.
|
||||
template.parentTemplate().$('.newWorkerButton').removeClass('active');
|
||||
},
|
||||
"click .actionRemove": function(event, template) {
|
||||
Meteor.call('deactivateWorker', this._id, function(error, result) {
|
||||
if(error) sAlert.error(error);
|
||||
else sAlert.success("Worker Deactivated");
|
||||
});
|
||||
},
|
||||
'click .actionActivate': function(event, template) {
|
||||
Meteor.call('reactivateWorker', this._id, function(error, result) {
|
||||
if(error) sAlert.error(error);
|
||||
else sAlert.success("Worker Reactivated");
|
||||
});
|
||||
},
|
||||
"click .actionShow": function(event, template) {
|
||||
Meteor.call('showWorker', this._id, function(error, result) {
|
||||
if(error) sAlert.error(error);
|
||||
else sAlert.success("Worker Visibility Enabled");
|
||||
});
|
||||
},
|
||||
'click .actionHide': function(event, template) {
|
||||
Meteor.call('hideWorker', this._id, function(error, result) {
|
||||
if(error) sAlert.error(error);
|
||||
else sAlert.success("Worker Visibility Disabled");
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
Template.WorkerEditor.onRendered(function() {
|
||||
this.$(".activitiesEditor").select2();
|
||||
});
|
||||
Template.WorkerEditor.helpers({
|
||||
activities: function() {
|
||||
let schema = Meteor.collections.Workers.simpleSchema();
|
||||
|
||||
return schema.constants.activities;
|
||||
},
|
||||
activitySelected: function() {
|
||||
let activity = this;
|
||||
let worker = Template.parentData();
|
||||
|
||||
return worker.activities && worker.activities.includes(activity) ? "selected" : "";
|
||||
}
|
||||
});
|
||||
Template.WorkerEditor.events({
|
||||
"click .editorCancel": function(event, template) {
|
||||
Session.set(PREFIX + "editedWorker", undefined);
|
||||
Session.set(PREFIX + 'displayNewWorker', false);
|
||||
template.parentTemplate().$('.newWorkerButton').removeClass('active');
|
||||
},
|
||||
"click .editorApply": function(event, template) {
|
||||
let name = template.$("input[name='name']").val().trim();
|
||||
let activities = template.$(".activitiesEditor").select2('data');
|
||||
let hourlyRate = template.$("input[name='hourlyRate']").val().trim();
|
||||
|
||||
activities = activities.map((n)=>n.id);
|
||||
hourlyRate = Number(hourlyRate);
|
||||
|
||||
if(Session.get(PREFIX + 'displayNewWorker')) {
|
||||
Meteor.call("createWorker", name, activities, hourlyRate, function(error, result) {
|
||||
if(error) sAlert.error(error);
|
||||
else {
|
||||
sAlert.success("Worker created.");
|
||||
Session.set(PREFIX + 'displayNewWorker', false);
|
||||
template.parentTemplate().$('.newWorkerButton').removeClass('active');
|
||||
}
|
||||
});
|
||||
}
|
||||
else {
|
||||
Meteor.call("updateWorker", this._id, name, activities, hourlyRate, function(error, result) {
|
||||
if(error) sAlert.error(error);
|
||||
else {
|
||||
sAlert.success("Worker updated.");
|
||||
Session.set(PREFIX + "editedWorker", undefined);
|
||||
template.parentTemplate().$('.newWorkerButton').removeClass('active');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
});
|
||||
@@ -1,65 +1,58 @@
|
||||
<template name="Auth_page">
|
||||
<div class="page auth">
|
||||
<nav>
|
||||
<div class="nav-group">
|
||||
<a href="#" class="js-menu nav-item">
|
||||
<span class="icon-list-unordered"></span>
|
||||
</a>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<div class="content-scrollable">
|
||||
<div class="wrapper-auth">
|
||||
{{> atForm}}
|
||||
</div>
|
||||
<template name="OverrideAtForm">
|
||||
{{#unless hide}}
|
||||
<div class="at-form">
|
||||
<!--{{#if showTitle}}-->
|
||||
<!--{{> atTitle}}-->
|
||||
<!--{{/if}}-->
|
||||
{{#if showOauthServices}}
|
||||
{{> atOauth}}
|
||||
{{/if}}
|
||||
{{#if showServicesSeparator}}
|
||||
{{> atSep}}
|
||||
{{/if}}
|
||||
{{#if showError}}
|
||||
{{> atError}}
|
||||
{{/if}}
|
||||
{{#if showResult}}
|
||||
{{> atResult}}
|
||||
{{/if}}
|
||||
{{#if showMessage}}
|
||||
{{> atMessage}}
|
||||
{{/if}}
|
||||
{{#if showPwdForm}}
|
||||
{{> OverrideAtPwdForm}}
|
||||
{{/if}}
|
||||
{{#if showTermsLink}}
|
||||
{{> atTermsLink}}
|
||||
{{/if}}
|
||||
{{#if showSignInLink}}
|
||||
{{> atSigninLink}}
|
||||
{{/if}}
|
||||
{{#if showSignUpLink}}
|
||||
{{> atSignupLink}}
|
||||
{{/if}}
|
||||
{{#if showResendVerificationEmailLink}}
|
||||
{{> atResendVerificationEmailLink}}
|
||||
{{/if}}
|
||||
</div>
|
||||
</div>
|
||||
{{/unless}}
|
||||
</template>
|
||||
|
||||
<template name="override-atPwdFormBtn">
|
||||
<button type="submit" class="btn-primary">
|
||||
{{buttonText}}
|
||||
</button>
|
||||
</template>
|
||||
|
||||
<template name="override-atTextInput">
|
||||
<div class="input {{#if isValidating}}validating{{/if}} {{#if hasError}}error{{/if}} {{#if hasSuccess}}has-success{{/if}} {{#if feedback}}has-feedback{{/if}}">
|
||||
<input type="{{type}}" id="at-field-{{_id}}" name="at-field-{{_id}}" placeholder="{{placeholder}}" autocapitalize="none" autocorrect="off">
|
||||
{{#if hasIcon}}
|
||||
<span class="{{iconClass}}"></span>
|
||||
{{/if}}
|
||||
{{#if hasError}}
|
||||
<span>{{errorText}}</span>
|
||||
{{/if}}
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<template name="override-atTitle">
|
||||
<h1 class="title-auth">{{title}}</h1>
|
||||
<p class="subtitle-auth">Signing in allows you to have private lists</p>
|
||||
</template>
|
||||
|
||||
<template name="override-atError">
|
||||
<div class="list-errors">
|
||||
{{#each error}}
|
||||
<div class="list-item">{{errorText}}</div>
|
||||
{{/each}}
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<template name="override-atPwdForm">
|
||||
<template name="OverrideAtPwdForm">
|
||||
<div class="at-pwd-form">
|
||||
<form role="form" id="at-pwd-form" class="{{disabled}}" novalidate action="#" method="POST">
|
||||
{{#each fields}}
|
||||
{{> atInput}}
|
||||
{{/each}}
|
||||
{{#if showReCaptcha}}
|
||||
{{> atReCaptcha}}
|
||||
{{/if}}
|
||||
{{> atPwdFormBtn}}
|
||||
{{#if showForgotPasswordLink}}
|
||||
{{> atPwdLink}}
|
||||
{{/if}}
|
||||
<form role="form" id="at-pwd-form" novalidate action="#" method="POST">
|
||||
<fieldset {{disabled}}>
|
||||
{{#each fields}}
|
||||
{{> atInput}}
|
||||
{{/each}}
|
||||
{{#if showReCaptcha}}
|
||||
{{> atReCaptcha}}
|
||||
{{/if}}
|
||||
{{> atPwdFormBtn}}
|
||||
{{#if showForgotPasswordLink}}
|
||||
{{> atPwdLink}}
|
||||
{{/if}}
|
||||
</fieldset>
|
||||
</form>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -2,6 +2,13 @@ import { Template } from 'meteor/templating';
|
||||
|
||||
import './accounts.html';
|
||||
|
||||
// Simply 'inherits' helpers from AccountsTemplates
|
||||
Template.OverrideAtForm.helpers(AccountsTemplates.atFormHelpers);
|
||||
|
||||
// Simply 'inherits' helpers and events from AccountsTemplates
|
||||
Template.OverrideAtPwdForm.helpers(AccountsTemplates.atPwdFormHelpers);
|
||||
Template.OverrideAtPwdForm.events(AccountsTemplates.atPwdFormEvents);
|
||||
|
||||
// We identified the templates that need to be overridden by looking at the available templates
|
||||
// here: https://github.com/meteor-useraccounts/unstyled/tree/master/lib
|
||||
// Template['override-atPwdFormBtn'].replaces('atPwdFormBtn');
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
<template name="Body">
|
||||
{{> sAlert}}
|
||||
<div id="mainBody" class="mainBody">
|
||||
<nav class="leftSidebarContainer">
|
||||
<a href="javascript:" class="fa fa-bars leftSidebarMenuButton" aria-hidden="true"></a>
|
||||
<nav class="leftSidebarContainer generalSidebar">
|
||||
<a href="javascript:" class="fa fa-balance-scale leftSidebarMenuButton generalMenuButton" aria-hidden="true"></a>
|
||||
<div class="leftSidebar">
|
||||
<div class="logoArea">
|
||||
<i class="fa fa-sign-out fa-2x signOut" aria-hidden="true"></i>
|
||||
@@ -10,7 +10,63 @@
|
||||
<img src="/images/PetitTetonLogo_v2.png"/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="menuArea">
|
||||
<div class="menuArea generalMenu">
|
||||
<ul>
|
||||
<li class="{{isActiveRoute 'Sales'}}">
|
||||
<a href="{{pathFor 'Sales'}}">
|
||||
Sales<!-- <a class="subMenu {{isActiveRoute 'SalesSheets'}}" href="{{pathFor 'SalesSheets'}}">Sales Sheets</a>-->
|
||||
</a>
|
||||
</li>
|
||||
<li class="{{isActiveRoute 'SalesSheets'}}">
|
||||
<a href="{{pathFor 'SalesSheets'}}">
|
||||
Sales Sheets
|
||||
</a>
|
||||
</li>
|
||||
<li class="{{isActiveRoute 'Production'}}">
|
||||
<a href="{{pathFor 'Production'}}">
|
||||
Production<!-- <span class="tag">sample</span>-->
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="footer">
|
||||
© Petit Teton LLC 2017
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
<nav class="leftSidebarContainer graphsSidebar">
|
||||
<a href="javascript:" class="fa fa-line-chart leftSidebarMenuButton graphsMenuButton" aria-hidden="true"></a>
|
||||
<div class="leftSidebar">
|
||||
<div class="logoArea">
|
||||
<i class="fa fa-sign-out fa-2x signOut" aria-hidden="true"></i>
|
||||
<div class="logo">
|
||||
<img src="/images/PetitTetonLogo_v2.png"/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="menuArea graphsMenu">
|
||||
<ul>
|
||||
<li class="{{isActiveRoute 'Graphs'}}">
|
||||
<a href="{{pathFor 'Graphs'}}">
|
||||
Graphs
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="footer">
|
||||
© Petit Teton LLC 2017
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
<nav class="leftSidebarContainer settingsSidebar">
|
||||
<a href="javascript:" class="fa fa-cogs leftSidebarMenuButton settingsMenuButton" aria-hidden="true"></a>
|
||||
<div class="leftSidebar">
|
||||
<div class="logoArea">
|
||||
<i class="fa fa-sign-out fa-2x signOut" aria-hidden="true"></i>
|
||||
<div class="logo">
|
||||
<img src="/images/PetitTetonLogo_v2.png"/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="menuArea settingsMenu">
|
||||
<ul>
|
||||
{{#if isInRole 'manage'}}
|
||||
<li class="{{isActiveRoute 'UserManagement'}}">
|
||||
@@ -24,21 +80,6 @@
|
||||
</a>
|
||||
</li>
|
||||
{{/if}}
|
||||
<li class="{{isActiveRoute 'Sales'}}">
|
||||
<a href="{{pathFor 'Sales'}}">
|
||||
Sales <a class="subMenu {{isActiveRoute 'SalesSheets'}}" href="{{pathFor 'SalesSheets'}}">Sales Sheets</a>
|
||||
</a>
|
||||
</li>
|
||||
<!--<li class="{{isActiveRoute 'SalesSheets'}}">-->
|
||||
<!--<a href="{{pathFor 'SalesSheets'}}">-->
|
||||
<!--Sales Sheets-->
|
||||
<!--</a>-->
|
||||
<!--</li>-->
|
||||
<li class="{{isActiveRoute 'Production'}}">
|
||||
<a href="{{pathFor 'Production'}}">
|
||||
Production<!-- <span class="tag">sample</span>-->
|
||||
</a>
|
||||
</li>
|
||||
<li class="{{isActiveRoute 'Products'}}">
|
||||
<a href="{{pathFor 'Products'}}">
|
||||
Products
|
||||
@@ -64,9 +105,9 @@
|
||||
Venues
|
||||
</a>
|
||||
</li>
|
||||
<li class="{{isActiveRoute 'Graphs'}}">
|
||||
<a href="{{pathFor 'Graphs'}}">
|
||||
Graphs
|
||||
<li class="{{isActiveRoute 'Workers'}}">
|
||||
<a href="{{pathFor 'Workers'}}">
|
||||
Workers
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
18
imports/ui/layouts/Body.import.styl
vendored
18
imports/ui/layouts/Body.import.styl
vendored
@@ -44,7 +44,6 @@
|
||||
.leftSidebarMenuButton
|
||||
position: absolute
|
||||
right: -30px
|
||||
top: 10px
|
||||
-webkit-transition: .5s ease-in
|
||||
-moz-transition: .5s ease-in
|
||||
-o-transition: .5s ease-in
|
||||
@@ -67,8 +66,22 @@
|
||||
padding: 5px 0
|
||||
background-color: #90b272
|
||||
display: block
|
||||
border-top: 1px solid #494
|
||||
border-right: 1px solid #494
|
||||
border-bottom: 1px solid #494
|
||||
.leftSidebarMenuButton:hover
|
||||
color: rgba(150,0,0,.5)
|
||||
nav.generalSidebar
|
||||
.leftSidebarMenuButton
|
||||
top: 10px
|
||||
nav.graphsSidebar
|
||||
.leftSidebarMenuButton
|
||||
top: 50px
|
||||
nav.settingsSidebar
|
||||
.leftSidebarMenuButton
|
||||
top: 90px
|
||||
nav.menuHide .leftSidebarMenuButton
|
||||
right: 60px
|
||||
nav.menuShow
|
||||
margin: 0
|
||||
nav.menuShow .leftSidebarMenuButton
|
||||
@@ -79,8 +92,9 @@
|
||||
-ms-transform: rotate(45deg) !important
|
||||
transform: rotate(45deg) !important
|
||||
-moz-border-radius-bottomright: 0
|
||||
border-top-right-radius: 0
|
||||
//border-top-right-radius: 0
|
||||
border-bottom-right-radius: 0
|
||||
border-bottom: 0
|
||||
.leftSidebar
|
||||
flex: 0 0 auto
|
||||
display: flex
|
||||
|
||||
@@ -1,19 +1,54 @@
|
||||
import { Template } from 'meteor/templating';
|
||||
import './Body.html';
|
||||
|
||||
Template.Body.toggleMenu = function($sidebar) {
|
||||
let $sidebars = $('nav.leftSidebarContainer');
|
||||
|
||||
for(let i = 0; i < $sidebars.length; i++) {
|
||||
if($sidebars[i] == $sidebar[0]) {
|
||||
$sidebar.toggleClass('menuShow');
|
||||
}
|
||||
else {
|
||||
$($sidebars[i]).toggleClass('menuHide');
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
Template.Body.events({
|
||||
"click .signOut": function(event, template) {
|
||||
AccountsTemplates.logout();
|
||||
},
|
||||
"click .leftSidebarMenuButton": function(event, template) {
|
||||
// General Menu
|
||||
"click .generalSidebar .leftSidebarMenuButton": function(event, template) {
|
||||
event.preventDefault();
|
||||
$('nav.leftSidebarContainer').toggleClass('menuShow');
|
||||
Template.Body.toggleMenu($('nav.generalSidebar'));
|
||||
},
|
||||
"click .menuArea a": function(event, template) {
|
||||
$('nav.leftSidebarContainer').toggleClass('menuShow');
|
||||
"click .generalSidebar .leftSidebar a": function(event, template) {
|
||||
Template.Body.toggleMenu($('nav.generalSidebar'));
|
||||
},
|
||||
"click .menuArea a .subMenu": function(event, template) {
|
||||
$('nav.leftSidebarContainer').toggleClass('menuShow');
|
||||
"click .generalSidebar .leftSidebar a .subMenu": function(event, template) {
|
||||
Template.Body.toggleMenu($('nav.generalSidebar'));
|
||||
},
|
||||
// Graphs Menu
|
||||
"click .graphsSidebar .leftSidebarMenuButton": function(event, template) {
|
||||
event.preventDefault();
|
||||
Template.Body.toggleMenu($('nav.graphsSidebar'));
|
||||
},
|
||||
"click .graphsSidebar .leftSidebar a": function(event, template) {
|
||||
Template.Body.toggleMenu($('nav.graphsSidebar'));
|
||||
},
|
||||
"click .graphsSidebar .leftSidebar a .subMenu": function(event, template) {
|
||||
Template.Body.toggleMenu($('nav.graphsSidebar'));
|
||||
},
|
||||
// Settings Menu
|
||||
"click .settingsSidebar .leftSidebarMenuButton": function(event, template) {
|
||||
event.preventDefault();
|
||||
Template.Body.toggleMenu($('nav.settingsSidebar'));
|
||||
},
|
||||
"click .settingsSidebar .leftSidebar a": function(event, template) {
|
||||
Template.Body.toggleMenu($('nav.settingsSidebar'));
|
||||
},
|
||||
"click .settingsSidebar .leftSidebar a .subMenu": function(event, template) {
|
||||
Template.Body.toggleMenu($('nav.settingsSidebar'));
|
||||
}
|
||||
});
|
||||
@@ -1,7 +0,0 @@
|
||||
<template name="Full">
|
||||
<div id="full" class="content">
|
||||
<div class="form">
|
||||
{{> Template.dynamic template=content}}
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
11
imports/ui/layouts/Full.import.styl
vendored
11
imports/ui/layouts/Full.import.styl
vendored
@@ -1,11 +0,0 @@
|
||||
#full.content
|
||||
//width: 100%
|
||||
//height: 100%
|
||||
//background-image: linear-gradient(to bottom, #315481, #918e82 100%)'
|
||||
background: white
|
||||
|
||||
.form
|
||||
//margin: 5% auto 0 auto
|
||||
margin: 0 auto
|
||||
width: 300px
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
import './Full.html';
|
||||
8
imports/ui/layouts/Login.html
Normal file
8
imports/ui/layouts/Login.html
Normal file
@@ -0,0 +1,8 @@
|
||||
<template name="Login">
|
||||
<div id="login" class="content">
|
||||
<div class="contentBox">
|
||||
<img src="/images/PetitTetonLogo_v2.png"/>
|
||||
<div class="form">{{> Template.dynamic template=content}}</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
64
imports/ui/layouts/Login.import.styl
vendored
Normal file
64
imports/ui/layouts/Login.import.styl
vendored
Normal file
@@ -0,0 +1,64 @@
|
||||
#login.content
|
||||
//width: 100%
|
||||
//height: 100%
|
||||
//background-image: linear-gradient(to bottom, #315481, #918e82 100%)'
|
||||
background: white
|
||||
.contentBox
|
||||
margin: 0 auto
|
||||
max-width: 600px
|
||||
background-color: #88d15a
|
||||
-webkit-box-shadow: 10px 10px 5px 0px rgba(0,0,0,0.5);
|
||||
-moz-box-shadow: 10px 10px 5px 0px rgba(0,0,0,0.5);
|
||||
box-shadow: 10px 10px 5px 0px rgba(0,0,0,0.5);
|
||||
margin-top: 5%
|
||||
padding: 40px 10px
|
||||
text-align: center
|
||||
img
|
||||
height: 120px
|
||||
vertical-align: top
|
||||
.form
|
||||
display: inline-block
|
||||
margin-left: 20px
|
||||
input
|
||||
padding: 8px
|
||||
width: 300px
|
||||
margin-bottom: 10px
|
||||
label
|
||||
display: none
|
||||
fieldset
|
||||
border: none
|
||||
.at-btn
|
||||
margin-bottom: 6px
|
||||
text-align: center
|
||||
width: 300px
|
||||
background: #34d955;
|
||||
background-image: -webkit-linear-gradient(top, #5d942b, #4b7d26)
|
||||
background-image: -moz-linear-gradient(top, #5d942b, #4b7d26)
|
||||
background-image: -ms-linear-gradient(top, #5d942b, #4b7d26)
|
||||
background-image: -o-linear-gradient(top, #5d942b, #4b7d26)
|
||||
background-image: linear-gradient(to bottom, #5d942b, #4b7d26)
|
||||
font-family: "Arial Black", Arial
|
||||
color: #ffffff
|
||||
font-size: 14px
|
||||
line-height: 16px
|
||||
padding: 10px 20px 10px 20px
|
||||
text-decoration: none
|
||||
text-transform: uppercase
|
||||
border: none
|
||||
.at-btn:hover
|
||||
background: #29b54f
|
||||
background-image: -webkit-linear-gradient(top, #29b54f, #186b31)
|
||||
background-image: -moz-linear-gradient(top, #29b54f, #186b31)
|
||||
background-image: -ms-linear-gradient(top, #29b54f, #186b31)
|
||||
background-image: -o-linear-gradient(top, #29b54f, #186b31)
|
||||
background-image: linear-gradient(to bottom, #29b54f, #186b31)
|
||||
text-decoration: none
|
||||
cursor: pointer
|
||||
.at-link
|
||||
color: #1555b4
|
||||
font: Arial
|
||||
font-size: 12px
|
||||
font-weight: 800
|
||||
text-decoration: none
|
||||
.at-link:hover
|
||||
text-decoration: underline
|
||||
1
imports/ui/layouts/Login.js
Normal file
1
imports/ui/layouts/Login.js
Normal file
@@ -0,0 +1 @@
|
||||
import './Login.html';
|
||||
Reference in New Issue
Block a user