Added custom scroll bars and separated the table header from the tables for Sales and the Graphs tables.
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -1,2 +1,3 @@
|
|||||||
node_modules
|
node_modules
|
||||||
|
.idea
|
||||||
private
|
private
|
||||||
@@ -14,3 +14,4 @@ notices-for-facebook-graph-api-2
|
|||||||
1.4.0-remove-old-dev-bundle-link
|
1.4.0-remove-old-dev-bundle-link
|
||||||
1.4.1-add-shell-server-package
|
1.4.1-add-shell-server-package
|
||||||
1.4.3-split-account-service-packages
|
1.4.3-split-account-service-packages
|
||||||
|
1.5-add-dynamic-import-package
|
||||||
|
|||||||
@@ -4,26 +4,25 @@
|
|||||||
# 'meteor add' and 'meteor remove' will edit this file for you,
|
# 'meteor add' and 'meteor remove' will edit this file for you,
|
||||||
# but you can also edit it by hand.
|
# but you can also edit it by hand.
|
||||||
|
|
||||||
meteor-base@1.0.4 # Packages every Meteor app needs to have
|
meteor-base@1.1.0 # Packages every Meteor app needs to have
|
||||||
mobile-experience@1.0.4 # Packages for a great mobile UX
|
mobile-experience@1.0.5 # Packages for a great mobile UX
|
||||||
mongo@1.1.17 # The database Meteor supports right now
|
mongo@1.2.2 # The database Meteor supports right now
|
||||||
blaze-html-templates@1.0.4 # Compile .html files into Meteor Blaze views
|
blaze-html-templates@1.0.4 # Compile .html files into Meteor Blaze views
|
||||||
reactive-var@1.0.11 # Reactive variable for tracker
|
reactive-var@1.0.11 # Reactive variable for tracker
|
||||||
reactive-dict@1.1.8 # ???
|
reactive-dict@1.1.9 # ???
|
||||||
jquery@1.11.10 # Helpful client-side library
|
|
||||||
tracker@1.1.3 # Meteor's client-side reactive programming library
|
tracker@1.1.3 # Meteor's client-side reactive programming library
|
||||||
tomwasd:history-polyfill # Adds IE 8/9 support for HTML5 history.
|
tomwasd:history-polyfill # Adds IE 8/9 support for HTML5 history.
|
||||||
email@1.2.1 # Adds the Meteor/Email package for sending lost password emails
|
email@1.2.3 # Adds the Meteor/Email package for sending lost password emails
|
||||||
|
|
||||||
standard-minifier-css@1.3.4 # CSS minifier run for production mode
|
standard-minifier-css@1.3.5 # CSS minifier run for production mode
|
||||||
standard-minifier-js@2.0.0 # JS minifier run for production mode
|
standard-minifier-js@2.1.2 # JS minifier run for production mode
|
||||||
es5-shim@4.6.15 # ECMAScript 5 compatibility for older browsers.
|
es5-shim@4.6.15 # ECMAScript 5 compatibility for older browsers.
|
||||||
poorvavyas:es6-shim
|
poorvavyas:es6-shim
|
||||||
ecmascript@0.7.3 # Enable ECMAScript2015+ syntax in app code
|
ecmascript@0.8.3 # Enable ECMAScript2015+ syntax in app code
|
||||||
|
|
||||||
#accounts-ui
|
#accounts-ui
|
||||||
#accounts-base
|
#accounts-base
|
||||||
accounts-password@1.3.6
|
accounts-password@1.4.0
|
||||||
useraccounts:core
|
useraccounts:core
|
||||||
useraccounts:bootstrap
|
useraccounts:bootstrap
|
||||||
useraccounts:flow-routing # Configures email flows. Used for AccountsTemplates class.
|
useraccounts:flow-routing # Configures email flows. Used for AccountsTemplates class.
|
||||||
@@ -34,7 +33,7 @@ arillo:flow-router-helpers # Provides various template helpers such as {{pathFo
|
|||||||
#tomwasd:flow-router-seo
|
#tomwasd:flow-router-seo
|
||||||
kadira:blaze-layout
|
kadira:blaze-layout
|
||||||
|
|
||||||
shell-server@0.2.3 # ???
|
shell-server@0.2.4 # ???
|
||||||
meteortoys:allthings
|
meteortoys:allthings
|
||||||
stylus@2.513.9
|
stylus@2.513.9
|
||||||
session@1.1.7
|
session@1.1.7
|
||||||
@@ -43,12 +42,12 @@ check@1.2.5 # Allows for checking the structure and types of arguments pas
|
|||||||
#audit-argument-checks # Used in combination with the Check package for checking the structure and types of arguments passed to Meteor methods and publications. Automatically alerts when a method or publication does not use a check() call.
|
#audit-argument-checks # Used in combination with the Check package for checking the structure and types of arguments passed to Meteor methods and publications. Automatically alerts when a method or publication does not use a check() call.
|
||||||
|
|
||||||
aldeed:simple-schema@1.5.3
|
aldeed:simple-schema@1.5.3
|
||||||
aldeed:collection2@2.10.0
|
aldeed:collection2
|
||||||
#matb33:collection-hooks # Allows the collections to register handlers that run before or after database interactions.
|
#matb33:collection-hooks # Allows the collections to register handlers that run before or after database interactions.
|
||||||
#zimme:collection-softremovable
|
#zimme:collection-softremovable
|
||||||
|
|
||||||
#aldeed:autoform@5.8.1
|
#aldeed:autoform@5.8.1
|
||||||
#aldeed:collection2-core@2.0.0
|
#aldeed:collection2-core@2.0.1
|
||||||
#aldeed:schema-deny # Addon for Collection2-core to use denyInsert or denyUpdate options.
|
#aldeed:schema-deny # Addon for Collection2-core to use denyInsert or denyUpdate options.
|
||||||
#aldeed:schema-index # Addon for Collection2-core to use index or unique options.
|
#aldeed:schema-index # Addon for Collection2-core to use index or unique options.
|
||||||
#skehoe1989:autoform-relations # Adds relations to autoform
|
#skehoe1989:autoform-relations # Adds relations to autoform
|
||||||
@@ -62,3 +61,6 @@ juliancwirko:s-alert # Client error/alert handling
|
|||||||
jcbernack:reactive-aggregate # Allows us to create a new client collection (from the server) with the contents being an aggregate of server data. Note that aggregation can only be done on the server currently as mini-mongo does not support it.
|
jcbernack:reactive-aggregate # Allows us to create a new client collection (from the server) with the contents being an aggregate of server data. Note that aggregation can only be done on the server currently as mini-mongo does not support it.
|
||||||
ostrio:logger
|
ostrio:logger
|
||||||
ostrio:loggermongo
|
ostrio:loggermongo
|
||||||
|
dynamic-import@0.1.3
|
||||||
|
markdown@1.0.12
|
||||||
|
wcrisman:jquery-custom-scrollbar
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
METEOR@1.4.4.2
|
METEOR@1.5.2.2
|
||||||
|
|||||||
131
.meteor/versions
131
.meteor/versions
@@ -1,41 +1,43 @@
|
|||||||
accounts-base@1.2.17
|
accounts-base@1.3.4
|
||||||
accounts-password@1.3.6
|
accounts-password@1.4.0
|
||||||
alanning:roles@1.2.15
|
alanning:roles@1.2.16
|
||||||
aldeed:collection2@2.10.0
|
aldeed:collection2@2.10.0
|
||||||
aldeed:collection2-core@1.2.0
|
aldeed:collection2-core@1.2.0
|
||||||
aldeed:schema-deny@1.1.0
|
aldeed:schema-deny@1.1.0
|
||||||
aldeed:schema-index@1.1.1
|
aldeed:schema-index@1.1.1
|
||||||
aldeed:simple-schema@1.5.3
|
aldeed:simple-schema@1.5.3
|
||||||
aldeed:template-extension@4.0.0
|
aldeed:template-extension@4.1.0
|
||||||
allow-deny@1.0.5
|
allow-deny@1.0.9
|
||||||
arillo:flow-router-helpers@0.5.2
|
arillo:flow-router-helpers@0.5.2
|
||||||
autoupdate@1.3.12
|
autoupdate@1.3.12
|
||||||
babel-compiler@6.18.2
|
babel-compiler@6.20.0
|
||||||
babel-runtime@1.0.1
|
babel-runtime@1.0.1
|
||||||
base64@1.0.10
|
base64@1.0.10
|
||||||
binary-heap@1.0.10
|
binary-heap@1.0.10
|
||||||
blaze@2.3.2
|
blaze@2.3.2
|
||||||
blaze-html-templates@1.0.5
|
blaze-html-templates@1.1.2
|
||||||
blaze-tools@1.0.10
|
blaze-tools@1.0.10
|
||||||
boilerplate-generator@1.0.11
|
boilerplate-generator@1.2.0
|
||||||
caching-compiler@1.1.9
|
caching-compiler@1.1.9
|
||||||
caching-html-compiler@1.0.7
|
caching-html-compiler@1.1.2
|
||||||
callback-hook@1.0.10
|
callback-hook@1.0.10
|
||||||
check@1.2.5
|
check@1.2.5
|
||||||
coffeescript@1.0.17
|
coffeescript@1.0.17
|
||||||
ddp@1.2.5
|
ddp@1.3.1
|
||||||
ddp-client@1.3.4
|
ddp-client@2.1.3
|
||||||
ddp-common@1.2.8
|
ddp-common@1.2.9
|
||||||
ddp-rate-limiter@1.0.7
|
ddp-rate-limiter@1.0.7
|
||||||
ddp-server@1.3.14
|
ddp-server@2.0.2
|
||||||
deps@1.0.12
|
deps@1.0.12
|
||||||
diff-sequence@1.0.7
|
diff-sequence@1.0.7
|
||||||
ecmascript@0.7.3
|
dynamic-import@0.1.3
|
||||||
ecmascript-runtime@0.3.15
|
ecmascript@0.8.3
|
||||||
ejson@1.0.13
|
ecmascript-runtime@0.4.1
|
||||||
email@1.2.1
|
ecmascript-runtime-client@0.4.3
|
||||||
|
ecmascript-runtime-server@0.4.1
|
||||||
|
ejson@1.0.14
|
||||||
|
email@1.2.3
|
||||||
es5-shim@4.6.15
|
es5-shim@4.6.15
|
||||||
fastclick@1.0.13
|
|
||||||
fortawesome:fontawesome@4.7.0
|
fortawesome:fontawesome@4.7.0
|
||||||
geojson-utils@1.0.10
|
geojson-utils@1.0.10
|
||||||
hot-code-push@1.0.4
|
hot-code-push@1.0.4
|
||||||
@@ -50,54 +52,58 @@ kadira:blaze-layout@2.3.0
|
|||||||
kadira:flow-router@2.12.1
|
kadira:flow-router@2.12.1
|
||||||
launch-screen@1.1.1
|
launch-screen@1.1.1
|
||||||
livedata@1.0.18
|
livedata@1.0.18
|
||||||
localstorage@1.0.12
|
localstorage@1.1.1
|
||||||
logging@1.1.17
|
logging@1.1.17
|
||||||
|
markdown@1.0.12
|
||||||
mdg:validation-error@0.2.0
|
mdg:validation-error@0.2.0
|
||||||
meteor@1.6.1
|
meteor@1.7.2
|
||||||
meteor-base@1.0.4
|
meteor-base@1.1.0
|
||||||
meteorhacks:aggregate@1.3.0
|
meteorhacks:aggregate@1.3.0
|
||||||
meteorhacks:collection-utils@1.2.0
|
meteorhacks:collection-utils@1.2.0
|
||||||
meteortoys:allthings@3.0.0
|
meteortoys:allthings@4.0.0
|
||||||
meteortoys:authenticate@3.0.0
|
meteortoys:authenticate@4.0.0
|
||||||
meteortoys:autopub@3.0.0
|
meteortoys:autopub@4.0.0
|
||||||
meteortoys:blueprint@3.0.0
|
meteortoys:blueprint@4.0.0
|
||||||
meteortoys:email@3.0.0
|
meteortoys:email@4.0.0
|
||||||
meteortoys:hotreload@3.0.0
|
meteortoys:hotreload@4.0.0
|
||||||
meteortoys:listen@3.0.0
|
meteortoys:listen@4.0.0
|
||||||
meteortoys:method@3.0.4
|
meteortoys:method@4.0.0
|
||||||
meteortoys:pub@3.0.4
|
meteortoys:mobile@4.0.0
|
||||||
meteortoys:result@3.0.0
|
meteortoys:pub@4.0.0
|
||||||
meteortoys:shell@3.0.0
|
meteortoys:result@4.0.0
|
||||||
meteortoys:status@3.0.0
|
meteortoys:shell@4.0.0
|
||||||
meteortoys:sub@3.0.0
|
meteortoys:status@4.0.0
|
||||||
meteortoys:throttle@3.0.0
|
meteortoys:sub@4.0.0
|
||||||
meteortoys:toykit@3.0.4
|
meteortoys:throttle@4.0.0
|
||||||
|
meteortoys:toggle@4.0.0
|
||||||
|
meteortoys:toykit@4.0.1
|
||||||
minifier-css@1.2.16
|
minifier-css@1.2.16
|
||||||
minifier-js@2.0.0
|
minifier-js@2.1.4
|
||||||
minimongo@1.0.23
|
minimongo@1.3.2
|
||||||
mizzao:bootboxjs@4.4.0
|
mizzao:bootboxjs@4.4.0
|
||||||
mobile-experience@1.0.4
|
mobile-experience@1.0.5
|
||||||
mobile-status-bar@1.0.14
|
mobile-status-bar@1.0.14
|
||||||
modules@0.8.2
|
modules@0.10.0
|
||||||
modules-runtime@0.7.10
|
modules-runtime@0.8.0
|
||||||
momentjs:moment@2.18.1
|
momentjs:moment@2.18.1
|
||||||
mongo@1.1.17
|
mongo@1.2.2
|
||||||
|
mongo-dev-server@1.0.1
|
||||||
mongo-id@1.0.6
|
mongo-id@1.0.6
|
||||||
mongo-livedata@1.0.12
|
mongo-livedata@1.0.12
|
||||||
msavin:jetsetter@2.0.0
|
msavin:jetsetter@4.0.0
|
||||||
msavin:mongol@2.0.1
|
msavin:mongol@4.0.1
|
||||||
npm-bcrypt@0.9.2
|
npm-bcrypt@0.9.3
|
||||||
npm-mongo@2.2.24
|
npm-mongo@2.2.30
|
||||||
observe-sequence@1.0.16
|
observe-sequence@1.0.16
|
||||||
ordered-dict@1.0.9
|
ordered-dict@1.0.9
|
||||||
ostrio:logger@1.1.2
|
ostrio:logger@2.0.3
|
||||||
ostrio:loggermongo@1.1.3
|
ostrio:loggermongo@2.0.1
|
||||||
poorvavyas:es6-shim@0.21.1
|
poorvavyas:es6-shim@0.21.1
|
||||||
promise@0.8.8
|
promise@0.9.0
|
||||||
raix:eventemitter@0.1.3
|
raix:eventemitter@0.1.3
|
||||||
random@1.0.10
|
random@1.0.10
|
||||||
rate-limit@1.0.8
|
rate-limit@1.0.8
|
||||||
reactive-dict@1.1.8
|
reactive-dict@1.1.9
|
||||||
reactive-var@1.0.11
|
reactive-var@1.0.11
|
||||||
reload@1.1.11
|
reload@1.1.11
|
||||||
retry@1.0.9
|
retry@1.0.9
|
||||||
@@ -105,26 +111,27 @@ routepolicy@1.0.12
|
|||||||
service-configuration@1.0.11
|
service-configuration@1.0.11
|
||||||
session@1.1.7
|
session@1.1.7
|
||||||
sha@1.0.9
|
sha@1.0.9
|
||||||
shell-server@0.2.3
|
shell-server@0.2.4
|
||||||
softwarerero:accounts-t9n@1.3.6
|
softwarerero:accounts-t9n@1.3.11
|
||||||
spacebars@1.0.13
|
spacebars@1.0.15
|
||||||
spacebars-compiler@1.1.1
|
spacebars-compiler@1.1.3
|
||||||
srp@1.0.10
|
srp@1.0.10
|
||||||
standard-minifier-css@1.3.4
|
standard-minifier-css@1.3.5
|
||||||
standard-minifier-js@2.0.0
|
standard-minifier-js@2.1.2
|
||||||
stylus@2.513.9
|
stylus@2.513.9
|
||||||
templating@1.2.15
|
templating@1.3.2
|
||||||
templating-compiler@1.2.15
|
templating-compiler@1.3.3
|
||||||
templating-runtime@1.2.15
|
templating-runtime@1.3.2
|
||||||
templating-tools@1.1.1
|
templating-tools@1.1.2
|
||||||
tomwasd:history-polyfill@0.0.1
|
tomwasd:history-polyfill@0.0.1
|
||||||
tracker@1.1.3
|
tracker@1.1.3
|
||||||
ui@1.0.12
|
ui@1.0.13
|
||||||
underscore@1.0.10
|
underscore@1.0.10
|
||||||
url@1.1.0
|
url@1.1.0
|
||||||
useraccounts:bootstrap@1.14.2
|
useraccounts:bootstrap@1.14.2
|
||||||
useraccounts:core@1.14.2
|
useraccounts:core@1.14.2
|
||||||
useraccounts:flow-routing@1.14.2
|
useraccounts:flow-routing@1.14.2
|
||||||
webapp@1.3.15
|
wcrisman:jquery-custom-scrollbar@3.0.0
|
||||||
|
webapp@1.3.19
|
||||||
webapp-hashing@1.0.9
|
webapp-hashing@1.0.9
|
||||||
zimme:active-route@2.3.2
|
zimme:active-route@2.3.2
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ import '/imports/util/select2/select2.full.js';
|
|||||||
import 'sweetalert2/dist/sweetalert2.min.css';
|
import 'sweetalert2/dist/sweetalert2.min.css';
|
||||||
import '/imports/util/simplegrid.css';
|
import '/imports/util/simplegrid.css';
|
||||||
import 'dragula/dist/dragula.css';
|
import 'dragula/dist/dragula.css';
|
||||||
|
//import 'malihu-custom-scrollbar-plugin/jquery.mCustomScrollbar.css';
|
||||||
|
|
||||||
Blaze._allowJavascriptUrls();
|
Blaze._allowJavascriptUrls();
|
||||||
|
|
||||||
|
|||||||
@@ -180,4 +180,5 @@ body
|
|||||||
@import "../imports/ui/SalesSheetEditor.import.styl"
|
@import "../imports/ui/SalesSheetEditor.import.styl"
|
||||||
@import "../imports/ui/Pricing.import.styl"
|
@import "../imports/ui/Pricing.import.styl"
|
||||||
@import "../imports/ui/Production.import.styl"
|
@import "../imports/ui/Production.import.styl"
|
||||||
@import "../imports/ui/Graphs.import.styl"
|
@import "../imports/ui/Graphs.import.styl"
|
||||||
|
@import "../imports/ui/TestList.import.styl"
|
||||||
@@ -1,5 +1,7 @@
|
|||||||
import { Meteor } from 'meteor/meteor';
|
import { Meteor } from 'meteor/meteor';
|
||||||
import { Mongo } from 'meteor/mongo';
|
import { Mongo } from 'meteor/mongo';
|
||||||
|
import { Logger } from 'meteor/ostrio:logger';
|
||||||
|
import { LoggerMongo } from 'meteor/ostrio:loggermongo';
|
||||||
|
|
||||||
// The logging tool is primarily for managing administrative functions such that administrators can view the app logs and issue commands that might generate administrative logging.
|
// The logging tool is primarily for managing administrative functions such that administrators can view the app logs and issue commands that might generate administrative logging.
|
||||||
|
|
||||||
|
|||||||
@@ -8,6 +8,10 @@ import {SimpleSchema} from 'meteor/aldeed:simple-schema';
|
|||||||
* The Product object has a prices field which is an object whose fields names are Measure ID's. Each field value (for each Measure ID) is an object that has a 'price', 'effectiveDate', and 'previousPrice'.
|
* The Product object has a prices field which is an object whose fields names are Measure ID's. Each field value (for each Measure ID) is an object that has a 'price', 'effectiveDate', and 'previousPrice'.
|
||||||
* The effectiveDate field stores the date as a number in the format YYYYMMDD. Converting this number into a local date is done with moment(sale.date.toString(), "YYYYMMDD").toDate(), and converting it to a number from a date can be accomplished with ~~(moment(date).format("YYYYMMDD")), where the ~~ is a bitwise not and converts a string to a number quickly and reliably.
|
* The effectiveDate field stores the date as a number in the format YYYYMMDD. Converting this number into a local date is done with moment(sale.date.toString(), "YYYYMMDD").toDate(), and converting it to a number from a date can be accomplished with ~~(moment(date).format("YYYYMMDD")), where the ~~ is a bitwise not and converts a string to a number quickly and reliably.
|
||||||
* Because the structure of the Product object is so complicated, the normal checking that is done by the framework cannot be used.
|
* Because the structure of the Product object is so complicated, the normal checking that is done by the framework cannot be used.
|
||||||
|
*
|
||||||
|
* A product is first deactivated, then hidden.
|
||||||
|
* A deactivated product is one that is no longer intended to be used (produced), but needs to show up in some lists because there may be some still floating around in inventory (needing to be sold). It should show with a yellow indicator if displayed.
|
||||||
|
* A product that is hidden is one that exists in the system as a historical artifact due to there still being data attached to it (sales for example). It should not normally show in lists, and should show up with a red indicator if it is displayed.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
Products = new Mongo.Collection('Products');
|
Products = new Mongo.Collection('Products');
|
||||||
@@ -190,6 +194,15 @@ if(Meteor.isServer) {
|
|||||||
}
|
}
|
||||||
else throw new Meteor.Error(403, "Not authorized.");
|
else throw new Meteor.Error(403, "Not authorized.");
|
||||||
},
|
},
|
||||||
|
convertProduct: function(productId, alternateProductId) {
|
||||||
|
if(Roles.userIsInRole(this.userId, [Meteor.UserRoles.ROLE_UPDATE])) {
|
||||||
|
check(productId, String);
|
||||||
|
check(alternateProductId, String);
|
||||||
|
// Replace all sale references to the given ID with the provided alternate product ID.
|
||||||
|
Meteor.collections.Sales.update({productId: productId}, {$set: {productId: alternateProductId}}, {multi: true});
|
||||||
|
}
|
||||||
|
else throw new Meteor.Error(403, "Not authorized.");
|
||||||
|
},
|
||||||
deactivateProduct: function(id) {
|
deactivateProduct: function(id) {
|
||||||
if(Roles.userIsInRole(this.userId, [Meteor.UserRoles.ROLE_UPDATE])) {
|
if(Roles.userIsInRole(this.userId, [Meteor.UserRoles.ROLE_UPDATE])) {
|
||||||
//Products.remove(id);
|
//Products.remove(id);
|
||||||
|
|||||||
@@ -16,6 +16,16 @@ let SalesSchema = new SimpleSchema({
|
|||||||
optional: false,
|
optional: false,
|
||||||
index: 1
|
index: 1
|
||||||
},
|
},
|
||||||
|
timestamp: {
|
||||||
|
type: Date,
|
||||||
|
label: "Timestamp",
|
||||||
|
optional: true
|
||||||
|
},
|
||||||
|
weekOfYear: {
|
||||||
|
type: Number,
|
||||||
|
label: "Week Of Year",
|
||||||
|
optional: true
|
||||||
|
},
|
||||||
amount: {
|
amount: {
|
||||||
type: Number,
|
type: Number,
|
||||||
label: "Amount",
|
label: "Amount",
|
||||||
@@ -189,7 +199,8 @@ if(Meteor.isServer) {
|
|||||||
let group = {
|
let group = {
|
||||||
$group: {
|
$group: {
|
||||||
_id: {
|
_id: {
|
||||||
year: {$dateToString: {format: '%Y', date: '$date'}}//{$year: '$date'}
|
//year: {$dateToString: {format: '%Y', date: '$date'}}//{$year: '$date'}
|
||||||
|
year: {$substr: ['$date', 0, 4]}
|
||||||
},
|
},
|
||||||
'total': {
|
'total': {
|
||||||
$sum: {
|
$sum: {
|
||||||
@@ -212,13 +223,13 @@ if(Meteor.isServer) {
|
|||||||
|
|
||||||
//Annual is assumed if not week or month.
|
//Annual is assumed if not week or month.
|
||||||
if(time === 'weekly') {
|
if(time === 'weekly') {
|
||||||
group.$group._id.week = {$dateToString: {format: '%U', date: '$date'}}; //{$week: '$date'};
|
group.$group._id.week = '$weekOfYear'; //{$dateToString: {format: '%U', date: new Date({$concat: [{$substr: ['$date', 0, 4]}, '-', {$substr: ['$date', 4, 2]}, '-', {$substr: ['$date', 6, 2]}]}) }}; //{$week: '$date'};
|
||||||
project.$project.week = '$_id.week';
|
project.$project.week = '$_id.week';
|
||||||
project.$project.date = {$concat: ['$_id.week', '-', '$_id.year']};
|
project.$project.date = {$concat: [{$substr: ['$_id.week', 0, 2]}, '-', '$_id.year']};
|
||||||
project.$project._id.$concat.push('$_id.week');
|
project.$project._id.$concat.push({$substr: ['$_id.week', 0, 2]});
|
||||||
}
|
}
|
||||||
else if(time === 'monthly') {
|
else if(time === 'monthly') {
|
||||||
group.$group._id.month = {$dateToString: {format: '%m', date: '$date'}}; //{$month: '$date'};
|
group.$group._id.month = {$substr: ['$date', 4, 6]}; //{$dateToString: {format: '%m', date: new Date({$concat: [{$substr: ['$date', 0, 4]}, '-', {$substr: ['$date', 4, 2]}, '-', {$substr: ['$date', 6, 2]}]}) }}; //{$month: '$date'};
|
||||||
project.$project.month = '$_id.month';
|
project.$project.month = '$_id.month';
|
||||||
project.$project.date = {$concat: ['$_id.month', '-', '$_id.year']};
|
project.$project.date = {$concat: ['$_id.month', '-', '$_id.year']};
|
||||||
project.$project._id.$concat.push('$_id.month');
|
project.$project._id.$concat.push('$_id.month');
|
||||||
@@ -285,7 +296,11 @@ if(Meteor.isServer) {
|
|||||||
comment: Match.Optional(String)
|
comment: Match.Optional(String)
|
||||||
});
|
});
|
||||||
|
|
||||||
|
let dateString = date.toString();
|
||||||
|
|
||||||
sale.createdAt = new Date();
|
sale.createdAt = new Date();
|
||||||
|
sale.timestamp = new Date(dateString.substring(0, 4) + "-" + dateString.substring(4, 6) + "-" + dateString.substring(6, 8) + "T00:00:00Z");
|
||||||
|
sale.weekOfYear = sale.timestamp.getWeek().toString();
|
||||||
|
|
||||||
if(Roles.userIsInRole(this.userId, [Meteor.UserRoles.ROLE_UPDATE])) {
|
if(Roles.userIsInRole(this.userId, [Meteor.UserRoles.ROLE_UPDATE])) {
|
||||||
Sales.insert(sale, function(err, id) {
|
Sales.insert(sale, function(err, id) {
|
||||||
@@ -332,8 +347,12 @@ if(Meteor.isServer) {
|
|||||||
check(price, Number);
|
check(price, Number);
|
||||||
check(amount, Number);
|
check(amount, Number);
|
||||||
|
|
||||||
|
let dateString = date.toString();
|
||||||
|
let timestamp = new Date(dateString.substring(0, 4) + "-" + dateString.substring(4, 6) + "-" + dateString.substring(6, 8) + "T00:00:00Z");
|
||||||
|
let weekOfYear = sale.timestamp.getWeek().toString();
|
||||||
|
|
||||||
if(Roles.userIsInRole(this.userId, [Meteor.UserRoles.ROLE_UPDATE])) {
|
if(Roles.userIsInRole(this.userId, [Meteor.UserRoles.ROLE_UPDATE])) {
|
||||||
Sales.update(id, {$set: {date, venueId, price, amount}}, function(err, id) {
|
Sales.update(id, {$set: {date, venueId, price, amount, timestamp, weekOfYear}}, function(err, id) {
|
||||||
if(err) console.log(err);
|
if(err) console.log(err);
|
||||||
}, {bypassCollection2: true});
|
}, {bypassCollection2: true});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -101,4 +101,11 @@ pri.route('/graphTest', {
|
|||||||
require("/imports/ui/GraphTest.js");
|
require("/imports/ui/GraphTest.js");
|
||||||
BlazeLayout.render('Body', {content: 'GraphTest'});
|
BlazeLayout.render('Body', {content: 'GraphTest'});
|
||||||
}
|
}
|
||||||
|
});
|
||||||
|
pri.route('/testList', {
|
||||||
|
name: 'TestList',
|
||||||
|
action: function(params, queryParams) {
|
||||||
|
require("/imports/ui/TestList.js");
|
||||||
|
BlazeLayout.render('Body', {content: 'TestList'});
|
||||||
|
}
|
||||||
});
|
});
|
||||||
@@ -1 +1,2 @@
|
|||||||
import "./email.js"
|
import "./email.js"
|
||||||
|
import "./../../util/polyfills/date.js"
|
||||||
@@ -13,7 +13,7 @@
|
|||||||
<option value="types">Types</option>
|
<option value="types">Types</option>
|
||||||
</select>
|
</select>
|
||||||
<svg class="salesGraph"></svg>
|
<svg class="salesGraph"></svg>
|
||||||
<div class="salesTable">
|
<div class="salesTableHeader">
|
||||||
<table class="table table-striped table-hover">
|
<table class="table table-striped table-hover">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
@@ -30,24 +30,50 @@
|
|||||||
<th class="total">Total</th>
|
<th class="total">Total</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
|
||||||
{{#each sales}}
|
|
||||||
<tr>
|
|
||||||
<td>{{year}}</td>
|
|
||||||
{{#if showTime "monthly"}}
|
|
||||||
<td>{{month}}</td>
|
|
||||||
{{/if}}
|
|
||||||
{{#if showTime "weekly"}}
|
|
||||||
<td>{{week}}</td>
|
|
||||||
{{/if}}
|
|
||||||
{{#if showOption "markets"}}
|
|
||||||
<td>{{venue}}</td>
|
|
||||||
{{/if}}
|
|
||||||
<td>{{formatTotal total}}</td>
|
|
||||||
</tr>
|
|
||||||
{{/each}}
|
|
||||||
</tbody>
|
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="listRow">
|
||||||
|
<div class="listCell">
|
||||||
|
<div class="salesTable mCustomScrollbar" data-mcs-theme="dark">
|
||||||
|
<table class="table table-striped table-hover">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th class="year"></th>
|
||||||
|
{{#if showTime "monthly"}}
|
||||||
|
<th class="month"></th>
|
||||||
|
{{/if}}
|
||||||
|
{{#if showTime "weekly"}}
|
||||||
|
<th class="week"></th>
|
||||||
|
{{/if}}
|
||||||
|
{{#if showOption "markets"}}
|
||||||
|
<th class="market"></th>
|
||||||
|
{{/if}}
|
||||||
|
<th class="total"></th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{{#each sales}}
|
||||||
|
<tr>
|
||||||
|
<td class="year">{{year}}</td>
|
||||||
|
{{#if showTime "monthly"}}
|
||||||
|
<td class="month">{{month}}</td>
|
||||||
|
{{/if}}
|
||||||
|
{{#if showTime "weekly"}}
|
||||||
|
<td class="week">{{week}}</td>
|
||||||
|
{{/if}}
|
||||||
|
{{#if showOption "markets"}}
|
||||||
|
<td class="market">{{venue}}</td>
|
||||||
|
{{/if}}
|
||||||
|
<td class="total">{{formatTotal total}}</td>
|
||||||
|
</tr>
|
||||||
|
{{/each}}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="spacerCell">
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
67
imports/ui/Graphs.import.styl
vendored
67
imports/ui/Graphs.import.styl
vendored
@@ -1,6 +1,10 @@
|
|||||||
#graphs
|
#graphs
|
||||||
margin: 10px 20px
|
display: table
|
||||||
|
content-box: border-box
|
||||||
|
padding: 10px 20px
|
||||||
height: 100%
|
height: 100%
|
||||||
|
width: 100%
|
||||||
|
text-align: left
|
||||||
svg
|
svg
|
||||||
width: 100%
|
width: 100%
|
||||||
.bar
|
.bar
|
||||||
@@ -17,22 +21,47 @@
|
|||||||
font-size: 14px
|
font-size: 14px
|
||||||
font-family: "Arial Black", "Arial Bold", Gadget, sans-serif
|
font-family: "Arial Black", "Arial Bold", Gadget, sans-serif
|
||||||
font-weight: 800
|
font-weight: 800
|
||||||
table
|
.table
|
||||||
table-layout: fixed
|
table-layout: fixed
|
||||||
> thead
|
.total
|
||||||
> tr
|
width: 200px
|
||||||
> th.total
|
.market
|
||||||
width: 200px
|
width: 200px
|
||||||
> th.market
|
.week
|
||||||
width: 200px
|
width: 200px
|
||||||
> th.week
|
.month
|
||||||
width: 200px
|
width: 200px
|
||||||
> th.month
|
.year
|
||||||
width: 200px
|
width: 200px
|
||||||
> th.year
|
.listRow
|
||||||
width: 200px
|
display: table-row
|
||||||
> tbody
|
.listCell
|
||||||
> tr.deactivated
|
display: table-cell
|
||||||
background-color: #fac0d1
|
position: relative
|
||||||
> tr.deactivated:hover
|
height: 100%
|
||||||
background-color: #ffcadb
|
width: auto
|
||||||
|
min-width: 300px
|
||||||
|
.salesTable
|
||||||
|
position: absolute
|
||||||
|
top: 0
|
||||||
|
bottom: 0
|
||||||
|
left: 0
|
||||||
|
right: 0
|
||||||
|
width: auto
|
||||||
|
height: auto
|
||||||
|
border: 0
|
||||||
|
font-size: 12.5px
|
||||||
|
overflow-y: scroll
|
||||||
|
.table
|
||||||
|
> thead
|
||||||
|
display: none
|
||||||
|
> tbody
|
||||||
|
> tr.deactivated
|
||||||
|
background-color: #fac0d1
|
||||||
|
> tr.deactivated:hover
|
||||||
|
background-color: #ffcadb
|
||||||
|
.spacerCell
|
||||||
|
display: table-cell
|
||||||
|
position: relative
|
||||||
|
width: 100%
|
||||||
|
height: 100%
|
||||||
@@ -28,6 +28,13 @@ Template.Graphs.onCreated(function() {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
Template.Graphs.onRendered(function() {
|
Template.Graphs.onRendered(function() {
|
||||||
|
|
||||||
|
$(".salesTable").mCustomScrollbar({
|
||||||
|
scrollButtons:{enable:true},
|
||||||
|
theme:"light-thick",
|
||||||
|
scrollbarPosition:"outside"
|
||||||
|
});
|
||||||
|
|
||||||
//Reset the pull downs to their former states.
|
//Reset the pull downs to their former states.
|
||||||
Template.instance().$('select[name="time"]').val(time);
|
Template.instance().$('select[name="time"]').val(time);
|
||||||
Template.instance().$('select[name="options"]').val(options);
|
Template.instance().$('select[name="options"]').val(options);
|
||||||
@@ -35,12 +42,12 @@ Template.Graphs.onRendered(function() {
|
|||||||
//Build the SVG Graphs
|
//Build the SVG Graphs
|
||||||
let margin = {top: 10, right: 0, bottom: 30, left: 40};
|
let margin = {top: 10, right: 0, bottom: 30, left: 40};
|
||||||
let width = 960 - margin.left - margin.right;
|
let width = 960 - margin.left - margin.right;
|
||||||
let height = 500 - margin.top - margin.bottom;
|
let height = 200 - margin.top - margin.bottom;
|
||||||
let x0Scale = d3.scaleBand().range([0, width]).padding(0.05);
|
let x0Scale = d3.scaleBand().range([0, width]).padding(0.05);
|
||||||
let x1Scale = d3.scaleBand().padding(0.05);
|
let x1Scale = d3.scaleBand().padding(0.05);
|
||||||
let yScale = d3.scaleLinear().range([height, 0]);
|
let yScale = d3.scaleLinear().range([height, 0]);
|
||||||
let svg = d3.select('svg.salesGraph')
|
let svg = d3.select('svg.salesGraph')
|
||||||
.attr("viewBox", "0 0 960 500")
|
.attr("viewBox", "0 0 960 200")
|
||||||
.attr("perserveAspectRatio", "xMidYMid meet")
|
.attr("perserveAspectRatio", "xMidYMid meet")
|
||||||
.append("g")
|
.append("g")
|
||||||
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
|
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
|
||||||
|
|||||||
@@ -7,6 +7,7 @@
|
|||||||
<a href="javascript:" class="clearLogs">Clear Logs</a><br/>
|
<a href="javascript:" class="clearLogs">Clear Logs</a><br/>
|
||||||
<a href="javascript:" class="countDuplicateSales">Count Duplicate Sales</a><br/>
|
<a href="javascript:" class="countDuplicateSales">Count Duplicate Sales</a><br/>
|
||||||
<a href="javascript:" class="deleteDuplicateSales">Delete Duplicate Sales</a><br/>
|
<a href="javascript:" class="deleteDuplicateSales">Delete Duplicate Sales</a><br/>
|
||||||
|
<a href="javascript:" class="enhanceSaleDates">Enhance Sale Dates (adds timestamp and weekOfYear to every Sale)</a><br/>
|
||||||
<div class="logCount">{{logCount}}</div>
|
<div class="logCount">{{logCount}}</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="pageContentRow">
|
<div class="pageContentRow">
|
||||||
|
|||||||
@@ -72,5 +72,8 @@ Template.MiscManagement.events({
|
|||||||
},
|
},
|
||||||
"click .deleteDuplicateSales": function(event, template) {
|
"click .deleteDuplicateSales": function(event, template) {
|
||||||
Meteor.call("deleteDuplicateSales");
|
Meteor.call("deleteDuplicateSales");
|
||||||
|
},
|
||||||
|
"click .enhanceSaleDates": function(event, template) {
|
||||||
|
Meteor.call("enhanceSalesDateFields");
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -49,17 +49,21 @@
|
|||||||
{{#if editing}}
|
{{#if editing}}
|
||||||
{{> ProductEditor}}
|
{{> ProductEditor}}
|
||||||
{{else}}
|
{{else}}
|
||||||
<td class="noselect nonclickable left">{{name}}</td>
|
{{#if converting}}
|
||||||
<td class="noselect nonclickable left">{{tags}}</td>
|
{{> ConvertProduct}}
|
||||||
<td class="noselect nonclickable left">{{aliases}}</td>
|
|
||||||
<td class="noselect nonclickable left">{{measures}}</td>
|
|
||||||
{{#if hidden}}
|
|
||||||
<td class="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}}
|
{{else}}
|
||||||
{{#if deactivated}}
|
<td class="noselect nonclickable left">{{name}}</td>
|
||||||
<td class="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>
|
<td class="noselect nonclickable left">{{tags}}</td>
|
||||||
|
<td class="noselect nonclickable left">{{aliases}}</td>
|
||||||
|
<td class="noselect nonclickable left">{{measures}}</td>
|
||||||
|
{{#if hidden}}
|
||||||
|
<td class="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> / <i class="actionConvert fa fa-exclamation-triangle fa-lg noselect clickable" title="Convert" aria-hidden="true"></i></td>
|
||||||
{{else}}
|
{{else}}
|
||||||
<td class="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 deactivated}}
|
||||||
|
<td class="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="center"><i class="actionEdit fa fa-pencil-square-o fa-lg noselect clickable" title="Edit" aria-hidden="true"></i> / <i class="actionDeactivate fa fa-times-circle fa-lg noselect clickable" title="Deactivate" aria-hidden="true"></i></td>
|
||||||
|
{{/if}}
|
||||||
{{/if}}
|
{{/if}}
|
||||||
{{/if}}
|
{{/if}}
|
||||||
{{/if}}
|
{{/if}}
|
||||||
@@ -71,29 +75,39 @@
|
|||||||
<div class="editorDiv"><label>Name:</label><input name="name" class="form-control" type="text" value="{{name}}" autocomplete="off" required></div>
|
<div class="editorDiv"><label>Name:</label><input name="name" class="form-control" type="text" value="{{name}}" autocomplete="off" required></div>
|
||||||
<div class="editorDiv"><label>Tags:</label>
|
<div class="editorDiv"><label>Tags:</label>
|
||||||
<select class="productTagsEditor" multiple="multiple">
|
<select class="productTagsEditor" multiple="multiple">
|
||||||
{{#each tags}}
|
{{#each tags}}
|
||||||
<option value="{{_id}}" {{tagSelected}}>{{name}}</option>
|
<option value="{{_id}}" {{tagSelected}}>{{name}}</option>
|
||||||
{{/each}}
|
{{/each}}
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
<div class="editorDiv"><label>Aliases:</label>
|
<div class="editorDiv"><label>Aliases:</label>
|
||||||
<select class="productAliasesEditor" multiple="multiple">
|
<select class="productAliasesEditor" multiple="multiple">
|
||||||
{{#each aliases}}
|
{{#each aliases}}
|
||||||
<option value="{{this}}" selected>{{this}}</option>
|
<option value="{{this}}" selected>{{this}}</option>
|
||||||
{{/each}}
|
{{/each}}
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
<div class="editorDiv"><label>Measures:</label>
|
<div class="editorDiv"><label>Measures:</label>
|
||||||
<select class="productMeasuresEditor" multiple="multiple">
|
<select class="productMeasuresEditor" multiple="multiple">
|
||||||
{{#each measures}}
|
{{#each measures}}
|
||||||
<option value="{{_id}}" {{measureSelected}}>{{name}}</option>
|
<option value="{{_id}}" {{measureSelected}}>{{name}}</option>
|
||||||
{{/each}}
|
{{/each}}
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
<td class="center productEditorTd"><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 productEditorTd"><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>
|
||||||
|
|
||||||
|
<template name="ConvertProduct">
|
||||||
|
<td>{{name}}</td>
|
||||||
|
<td colspan="3" class="convertProductTd">
|
||||||
|
<label class='control-label'>Alternate Product</label>
|
||||||
|
<input name="product" class="form-control" type="text" required/>
|
||||||
|
<label><em>Convert sales from this product to an alternate.</em></label>
|
||||||
|
</td>
|
||||||
|
<td class="center productEditorTd"><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="ProductSearch">
|
<template name="ProductSearch">
|
||||||
<div class="productSearch">
|
<div class="productSearch">
|
||||||
<input type="text" class="searchInput" placeholder="Filter..." value="{{searchValue}}"/>
|
<input type="text" class="searchInput" placeholder="Filter..." value="{{searchValue}}"/>
|
||||||
|
|||||||
2
imports/ui/Products.import.styl
vendored
2
imports/ui/Products.import.styl
vendored
@@ -109,5 +109,7 @@
|
|||||||
color: #0101e4
|
color: #0101e4
|
||||||
.actionShow
|
.actionShow
|
||||||
color: #027905
|
color: #027905
|
||||||
|
.actionConvert
|
||||||
|
color: #fb0909
|
||||||
> tr.hidden:hover
|
> tr.hidden:hover
|
||||||
background-color: #ffb5ff
|
background-color: #ffb5ff
|
||||||
@@ -72,6 +72,7 @@ Template.Products.events({
|
|||||||
else {
|
else {
|
||||||
Session.set(PREFIX + 'displayNewProduct', true);
|
Session.set(PREFIX + 'displayNewProduct', true);
|
||||||
Session.set(PREFIX + "editedProduct", undefined); //Clear the edited product so that only one editor is open at a time.
|
Session.set(PREFIX + "editedProduct", undefined); //Clear the edited product so that only one editor is open at a time.
|
||||||
|
Session.set(PREFIX + "convertedProduct", undefined); //Clear the converted product so that only one editor is open at a time.
|
||||||
}
|
}
|
||||||
template.$('.newProductButton').toggleClass('active');
|
template.$('.newProductButton').toggleClass('active');
|
||||||
},
|
},
|
||||||
@@ -166,6 +167,11 @@ Template.Product.helpers({
|
|||||||
|
|
||||||
return editedProduct == this._id;
|
return editedProduct == this._id;
|
||||||
},
|
},
|
||||||
|
converting: function() {
|
||||||
|
let convertedProduct = Session.get(PREFIX + "convertedProduct");
|
||||||
|
|
||||||
|
return convertedProduct == this._id;
|
||||||
|
},
|
||||||
getRowClass: function() {
|
getRowClass: function() {
|
||||||
return this.hidden ? "hidden" : this.deactivated ? "deactivated" : "";
|
return this.hidden ? "hidden" : this.deactivated ? "deactivated" : "";
|
||||||
}
|
}
|
||||||
@@ -174,9 +180,10 @@ Template.Product.events({
|
|||||||
"click .actionEdit": function(event, template) {
|
"click .actionEdit": function(event, template) {
|
||||||
Session.set(PREFIX + "editedProduct", this._id);
|
Session.set(PREFIX + "editedProduct", this._id);
|
||||||
Session.set(PREFIX + 'displayNewProduct', false); //Ensure the new product editor is closed.
|
Session.set(PREFIX + 'displayNewProduct', false); //Ensure the new product editor is closed.
|
||||||
|
Session.set(PREFIX + "convertedProduct", undefined); //Clear the converted product so that only one editor is open at a time.
|
||||||
template.$('.newProductButton').removeClass('active');
|
template.$('.newProductButton').removeClass('active');
|
||||||
},
|
},
|
||||||
"click .actionRemove": function(event, template) {
|
"click .actionDeactivate": function(event, template) {
|
||||||
Meteor.call('deactivateProduct', this._id, function(error, result) {
|
Meteor.call('deactivateProduct', this._id, function(error, result) {
|
||||||
if(error) sAlert.error(error);
|
if(error) sAlert.error(error);
|
||||||
else sAlert.success("Product Deactivated");
|
else sAlert.success("Product Deactivated");
|
||||||
@@ -194,6 +201,12 @@ Template.Product.events({
|
|||||||
else sAlert.success("Product Visibility Enabled");
|
else sAlert.success("Product Visibility Enabled");
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
"click .actionConvert": function(event, template) {
|
||||||
|
Session.set(PREFIX + "convertedProduct", this._id);
|
||||||
|
Session.set(PREFIX + 'displayNewProduct', false); //Ensure the new product editor is closed.
|
||||||
|
Session.set(PREFIX + "editedProduct", undefined); //Clear the edited product so that only one editor is open at a time.
|
||||||
|
template.$('.newProductButton').removeClass('active');
|
||||||
|
},
|
||||||
'click .actionHide': function(event, template) {
|
'click .actionHide': function(event, template) {
|
||||||
Meteor.call('hideProduct', this._id, function(error, result) {
|
Meteor.call('hideProduct', this._id, function(error, result) {
|
||||||
if(error) sAlert.error(error);
|
if(error) sAlert.error(error);
|
||||||
@@ -268,4 +281,35 @@ Template.ProductEditor.events({
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Template.ConvertProduct.onCreated(function() {
|
||||||
|
this.selectedProduct = new ReactiveVar();
|
||||||
|
});
|
||||||
|
Template.ConvertProduct.onRendered(function() {
|
||||||
|
this.$('[name="product"]').buildCombo({cursor: Meteor.collections.Products.find({$or: [{hidden: false}, {hidden: {$exists:false}}]}), selection: this.selectedProduct, textAttr: 'name', listClass: 'comboList', getClasses: function(data) {
|
||||||
|
return (data && data.deactivated) ? "deactivated" : "";
|
||||||
|
}});
|
||||||
|
});
|
||||||
|
Template.ConvertProduct.events({
|
||||||
|
"click .editorCancel": function(event, template) {
|
||||||
|
Session.set(PREFIX + "editedProduct", undefined);
|
||||||
|
Session.set(PREFIX + "convertedProduct", undefined);
|
||||||
|
Session.set(PREFIX + 'displayNewProduct', false);
|
||||||
|
template.parentTemplate().$('.newProductButton').removeClass('active');
|
||||||
|
},
|
||||||
|
"click .editorApply": function(event, template) {
|
||||||
|
let productId = template.selectedProduct.get()._id;
|
||||||
|
|
||||||
|
Meteor.call("convertProduct", this._id, productId, function(error, result) {
|
||||||
|
if(error) sAlert.error(error);
|
||||||
|
else {
|
||||||
|
sAlert.success("Sales of this product were converted.");
|
||||||
|
Session.set(PREFIX + "editedProduct", undefined);
|
||||||
|
Session.set(PREFIX + "convertedProduct", undefined);
|
||||||
|
Session.set(PREFIX + 'displayNewProduct', false);
|
||||||
|
template.parentTemplate().$('.newProductButton').removeClass('active');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
});
|
});
|
||||||
@@ -16,20 +16,36 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="separatedTableHeader">
|
||||||
|
<table class="table table-striped table-hover">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th class="amount noselect nonclickable">Amount {{>SaleSearch columnName='amount' width='90%'}}</th>
|
||||||
|
<th class="product noselect nonclickable">Product <br/>{{>SaleSearch columnName='productId' collectionQueryColumnName='name' collection='Products' collectionResultColumnName='_id' width='90%'}}</th>
|
||||||
|
<th class="price noselect nonclickable">Price {{>SaleSearch columnName='price' width='90%'}}</th>
|
||||||
|
<th class="measure noselect nonclickable">Measure {{>SaleSearch columnName='measureId' collectionQueryColumnName='name' collection='Measures' collectionResultColumnName='_id' width='90%'}}</th>
|
||||||
|
<th class="saleDate noselect nonclickable">Date (Week) {{>DateRangeSearch columnName='date' width='90%'}}</th>
|
||||||
|
<th class="createdDate noselect nonclickable">Created On</th>
|
||||||
|
<th class="venue noselect nonclickable">Venue {{>SaleSearch columnName='venueId' collectionQueryColumnName='name' collection='Venues' collectionResultColumnName='_id' width='90%'}}</th>
|
||||||
|
<th class="actions noselect nonclickable">Actions <span class="newSaleButton btn btn-success" title="Create Sale"><i class="fa fa-plus-circle" aria-hidden="true"></i><i class="fa fa-times-circle" aria-hidden="true"></i></span> <i class="fa fa-commenting fa-lg showOnlyComments clickable" title="Show Commented Sales" aria-hidden="true"></i></th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
<div class="listRow">
|
<div class="listRow">
|
||||||
<div class="listCell">
|
<div class="listCell">
|
||||||
<div class="tableContainer">
|
<div class="tableContainer mCustomScrollbar" data-mcs-theme="dark">
|
||||||
<table class="table table-striped table-hover">
|
<table class="table table-striped table-hover">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th class="amount noselect nonclickable">Amount {{>SaleSearch columnName='amount' width='90%'}}</th>
|
<th class="amount"></th>
|
||||||
<th class="product noselect nonclickable">Product <br/>{{>SaleSearch columnName='productId' collectionQueryColumnName='name' collection='Products' collectionResultColumnName='_id' width='90%'}}</th>
|
<th class="product"></th>
|
||||||
<th class="price noselect nonclickable">Price {{>SaleSearch columnName='price' width='90%'}}</th>
|
<th class="price"></th>
|
||||||
<th class="measure noselect nonclickable">Measure {{>SaleSearch columnName='measureId' collectionQueryColumnName='name' collection='Measures' collectionResultColumnName='_id' width='90%'}}</th>
|
<th class="measure"></th>
|
||||||
<th class="saleDate noselect nonclickable">Date (Week) {{>DateRangeSearch columnName='date' width='90%'}}</th>
|
<th class="saleDate"></th>
|
||||||
<th class="createdDate noselect nonclickable">Created On</th>
|
<th class="createdDate"></th>
|
||||||
<th class="venue noselect nonclickable">Venue {{>SaleSearch columnName='venueId' collectionQueryColumnName='name' collection='Venues' collectionResultColumnName='_id' width='90%'}}</th>
|
<th class="venue"></th>
|
||||||
<th class="actions noselect nonclickable">Actions <span class="newSaleButton btn btn-success" title="Create Sale"><i class="fa fa-plus-circle" aria-hidden="true"></i><i class="fa fa-times-circle" aria-hidden="true"></i></span> <i class="fa fa-commenting fa-lg showOnlyComments clickable" title="Show Commented Sales" aria-hidden="true"></i></th>
|
<th class="actions"></th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
|
|||||||
93
imports/ui/Sales.import.styl
vendored
93
imports/ui/Sales.import.styl
vendored
@@ -19,6 +19,54 @@
|
|||||||
padding: 4px 8px
|
padding: 4px 8px
|
||||||
margin: 4px 12px 4px 8px
|
margin: 4px 12px 4px 8px
|
||||||
display: table-cell
|
display: table-cell
|
||||||
|
.table
|
||||||
|
table-layout: fixed
|
||||||
|
min-width: 100%
|
||||||
|
thead
|
||||||
|
> tr
|
||||||
|
> th.amount
|
||||||
|
width: 90px
|
||||||
|
> th.product
|
||||||
|
width: auto
|
||||||
|
min-width: 140px
|
||||||
|
> th.price
|
||||||
|
width: 140px
|
||||||
|
> th.measure
|
||||||
|
width: 100px
|
||||||
|
> th.saleDate
|
||||||
|
width: 140px
|
||||||
|
> th.createdDate
|
||||||
|
width: 100px
|
||||||
|
> th.venue
|
||||||
|
width: 160px
|
||||||
|
> th.actions
|
||||||
|
width: 90px
|
||||||
|
.separatedTableHeader
|
||||||
|
table
|
||||||
|
thead
|
||||||
|
> tr
|
||||||
|
> th.actions
|
||||||
|
.newSaleButton
|
||||||
|
padding: 0px 12px
|
||||||
|
.fa-plus-circle
|
||||||
|
display: inline-block
|
||||||
|
.fa-times-circle
|
||||||
|
display: none
|
||||||
|
.newSaleButton:active
|
||||||
|
background-color: #fb557b
|
||||||
|
color: black
|
||||||
|
.fa-times-circle
|
||||||
|
display: inline-block
|
||||||
|
.fa-plus-circle
|
||||||
|
display: none
|
||||||
|
.showOnlyComments
|
||||||
|
color: #bcb95f
|
||||||
|
padding: 4px 8px
|
||||||
|
.showOnlyComments:hover
|
||||||
|
color: white
|
||||||
|
text-shadow: 0px 0px 10px #ff6d1f
|
||||||
|
.showOnlyComments.on
|
||||||
|
color: white
|
||||||
.listRow
|
.listRow
|
||||||
display: table-row
|
display: table-row
|
||||||
.listCell
|
.listCell
|
||||||
@@ -38,14 +86,12 @@
|
|||||||
//margin-bottom: 20px
|
//margin-bottom: 20px
|
||||||
border: 0
|
border: 0
|
||||||
font-size: 12.5px
|
font-size: 12.5px
|
||||||
overflow-y: auto
|
overflow-y: scroll
|
||||||
//height: 100%
|
//height: 100%
|
||||||
label
|
label
|
||||||
font-size: 10px
|
font-size: 10px
|
||||||
font-weight: 800
|
font-weight: 800
|
||||||
table
|
table
|
||||||
table-layout: fixed
|
|
||||||
min-width: 100%
|
|
||||||
.saleRemove
|
.saleRemove
|
||||||
color: red
|
color: red
|
||||||
margin-left: 8px
|
margin-left: 8px
|
||||||
@@ -57,45 +103,8 @@
|
|||||||
.editorCancel
|
.editorCancel
|
||||||
color: red
|
color: red
|
||||||
thead
|
thead
|
||||||
> tr
|
visibility: hidden
|
||||||
> th.amount
|
display: none
|
||||||
width: 90px
|
|
||||||
> th.product
|
|
||||||
width: auto
|
|
||||||
min-width: 140px
|
|
||||||
> th.price
|
|
||||||
width: 140px
|
|
||||||
> th.measure
|
|
||||||
width: 100px
|
|
||||||
> th.saleDate
|
|
||||||
width: 140px
|
|
||||||
> th.createdDate
|
|
||||||
width: 100px
|
|
||||||
> th.venue
|
|
||||||
width: 160px
|
|
||||||
> th.actions
|
|
||||||
width: 90px
|
|
||||||
.newSaleButton
|
|
||||||
padding: 0px 12px
|
|
||||||
.fa-plus-circle
|
|
||||||
display: inline-block
|
|
||||||
.fa-times-circle
|
|
||||||
display: none
|
|
||||||
.newSaleButton:active
|
|
||||||
background-color: #fb557b
|
|
||||||
color: black
|
|
||||||
.fa-times-circle
|
|
||||||
display: inline-block
|
|
||||||
.fa-plus-circle
|
|
||||||
display: none
|
|
||||||
.showOnlyComments
|
|
||||||
color: #bcb95f
|
|
||||||
padding: 4px 8px
|
|
||||||
.showOnlyComments:hover
|
|
||||||
color: white
|
|
||||||
text-shadow: 0px 0px 10px #ff6d1f
|
|
||||||
.showOnlyComments.on
|
|
||||||
color: white
|
|
||||||
.editComment
|
.editComment
|
||||||
color: grey
|
color: grey
|
||||||
.hasComment
|
.hasComment
|
||||||
|
|||||||
@@ -2,6 +2,9 @@
|
|||||||
import './Sales.html';
|
import './Sales.html';
|
||||||
import '/imports/util/selectize/selectize.js';
|
import '/imports/util/selectize/selectize.js';
|
||||||
import swal from 'sweetalert2';
|
import swal from 'sweetalert2';
|
||||||
|
//import 'malihu-custom-scrollbar-plugin/jquery.mCustomScrollbar.concat.min.js';
|
||||||
|
//import 'jquery-mousewheel';
|
||||||
|
//import 'malihu-custom-scrollbar-plugin';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Notes:
|
* Notes:
|
||||||
@@ -34,6 +37,20 @@ Template.Sales.onCreated(function() {
|
|||||||
Session.set(PREFIX + 'saleCount', Meteor.call('getSalesCount', Session.get(PREFIX + 'searchQuery')));
|
Session.set(PREFIX + 'saleCount', Meteor.call('getSalesCount', Session.get(PREFIX + 'searchQuery')));
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
Template.Sales.onRendered(function() {
|
||||||
|
//$('.tableContainer').mCustomScrollbar();
|
||||||
|
$(".tableContainer").mCustomScrollbar({
|
||||||
|
scrollButtons:{enable:true},
|
||||||
|
theme:"light-thick",
|
||||||
|
scrollbarPosition:"outside"
|
||||||
|
});
|
||||||
|
//(function($){
|
||||||
|
// $(window).on("load",function(){
|
||||||
|
// $(".tableContainer").mCustomScrollbar();
|
||||||
|
// });
|
||||||
|
//})(jQuery);
|
||||||
|
//$('#test').mCustomScrollbar();
|
||||||
|
});
|
||||||
Template.Sales.onDestroyed(function() {
|
Template.Sales.onDestroyed(function() {
|
||||||
if(Template.Sales.salesSubscription) {
|
if(Template.Sales.salesSubscription) {
|
||||||
Template.Sales.salesSubscription.stop();
|
Template.Sales.salesSubscription.stop();
|
||||||
|
|||||||
@@ -11,10 +11,15 @@
|
|||||||
<!-- ******** The Sheet Editor's Product Selector ********* -->
|
<!-- ******** The Sheet Editor's Product Selector ********* -->
|
||||||
|
|
||||||
<template name="SalesSheetEditorProductSelection">
|
<template name="SalesSheetEditorProductSelection">
|
||||||
<div class="salesSheetEditorProductSelectionControls vscFixed">
|
<div class="tableControls vscFixed">
|
||||||
<span class="button showAlternateNames">Alt. Names</span>
|
<span class="controlLabel">Show Hidden</span>
|
||||||
<i class="fa fa-question-circle clickable noselect" aria-hidden="true"></i>
|
<div class="toggleShowHidden checkbox checkbox-slider--b-flat">
|
||||||
<label>Filter </label><input class="form-control" type="text" name="productFilter" autocomplete="off"/>
|
<label>
|
||||||
|
<input type="checkbox" name="showHidden"><span></span>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<div style="margin-bottom: 6px"><label>Filter </label><input class="form-control" type="text" name="productFilter" autocomplete="off"/></div>
|
||||||
|
<i class="fa fa-times-circle clickable noselect clearFilter" style="margin-bottom: 5px" aria-hidden="true"></i>
|
||||||
</div>
|
</div>
|
||||||
<div class="selectionProductsListing columnContainer">
|
<div class="selectionProductsListing columnContainer">
|
||||||
{{#each products}}
|
{{#each products}}
|
||||||
@@ -31,9 +36,9 @@
|
|||||||
{{else}}
|
{{else}}
|
||||||
<i class="fa fa-circle-o" aria-hidden="true"></i>
|
<i class="fa fa-circle-o" aria-hidden="true"></i>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
<span class="productName noselect">{{name}}</span>
|
<span class="productName noselect {{#if hidden}}hidden{{/if}} {{#if deactivated}}deactivated{{/if}}">{{name}}</span>
|
||||||
</span>
|
</span>
|
||||||
{{#if sheetProduct}}
|
{{#if showAlternateName}}
|
||||||
<div class="includeAs"> as "{{sheetProductName}}"</div>
|
<div class="includeAs"> as "{{sheetProductName}}"</div>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
</div>
|
</div>
|
||||||
@@ -42,11 +47,17 @@
|
|||||||
<!-- ******** The Sheet Editor's Configuration ********* -->
|
<!-- ******** The Sheet Editor's Configuration ********* -->
|
||||||
<!-- The overall Sheet Configuration Editor. Contains multiple rows in columns, one row for each PRODUCT or HEADER. -->
|
<!-- The overall Sheet Configuration Editor. Contains multiple rows in columns, one row for each PRODUCT or HEADER. -->
|
||||||
<template name="SalesSheetEditorConfiguration">
|
<template name="SalesSheetEditorConfiguration">
|
||||||
<div class="vscFixed configurationControls">
|
<div class="vscFixed tableControls">
|
||||||
<div class="heading columnContent noselect">
|
<div class="heading columnContent noselect">
|
||||||
<div class="name clickable">New Heading</div>
|
<div class="name clickable" title="Drag to the list to create a new heading.">New Heading</div>
|
||||||
<div class="nameEditor"><input name="name" tabindex="1" value="New Heading"/> <i class="fa fa-check-circle accept" aria-hidden="true"></i> <i class="fa fa-times-circle reject" aria-hidden="true"></i></div>
|
<div class="nameEditor"><input name="name" tabindex="1" value="New Heading"/> <i class="fa fa-check-circle accept" aria-hidden="true"></i> <i class="fa fa-times-circle reject" aria-hidden="true"></i></div>
|
||||||
</div>
|
</div>
|
||||||
|
<span class="controlLabel" title="Show/Hide the measures available for each product.">Show Measures</span>
|
||||||
|
<div class="toggleShowHidden checkbox checkbox-slider--b-flat" title="Show/Hide the measures available for each product.">
|
||||||
|
<label>
|
||||||
|
<input type="checkbox" name="showMeasures"><span></span>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="configurationProductsListing columnContainer">
|
<div class="configurationProductsListing columnContainer">
|
||||||
{{#each products}}
|
{{#each products}}
|
||||||
@@ -61,11 +72,13 @@
|
|||||||
<div class="product columnContent noselect" data-model="{{productId}}">
|
<div class="product columnContent noselect" data-model="{{productId}}">
|
||||||
<div class="name clickable">{{name}}</div>
|
<div class="name clickable">{{name}}</div>
|
||||||
<div class="nameEditor"><input class="form-control" name="name" type="text" tabindex="1" value="{{name}}"/> <i class="fa fa-check-circle accept" aria-hidden="true"></i> <i class="fa fa-times-circle reject" aria-hidden="true"></i></div>
|
<div class="nameEditor"><input class="form-control" name="name" type="text" tabindex="1" value="{{name}}"/> <i class="fa fa-check-circle accept" aria-hidden="true"></i> <i class="fa fa-times-circle reject" aria-hidden="true"></i></div>
|
||||||
<div class="measures">
|
{{#if showMeasures}}
|
||||||
{{#each measureId in measures}}
|
<div class="measures">
|
||||||
<span class="measureButton button {{#if isSelected measureId}}selected{{/if}}" data-model="{{measureId}}">{{measureName measureId}}</span>
|
{{#each measureId in measures}}
|
||||||
{{/each}}
|
<span class="measureButton button {{#if isSelected measureId}}selected{{/if}}" data-model="{{measureId}}">{{measureName measureId}}</span>
|
||||||
</div>
|
{{/each}}
|
||||||
|
</div>
|
||||||
|
{{/if}}
|
||||||
</div>
|
</div>
|
||||||
{{else}} {{! HEADING }}
|
{{else}} {{! HEADING }}
|
||||||
<div class="heading columnContent noselect">
|
<div class="heading columnContent noselect">
|
||||||
|
|||||||
69
imports/ui/SalesSheetEditor.import.styl
vendored
69
imports/ui/SalesSheetEditor.import.styl
vendored
@@ -1,21 +1,58 @@
|
|||||||
#salesSheetsMain
|
#salesSheetsMain
|
||||||
.salesSheetEditorControls
|
.salesSheetEditorControls
|
||||||
margin-bottom: 8px
|
margin-bottom: 8px
|
||||||
.salesSheetEditorProductSelectionControls
|
.tableControls
|
||||||
margin-bottom: 8px
|
margin-bottom: 8px
|
||||||
width: 100%
|
width: 100%
|
||||||
text-align: right
|
//text-align: left
|
||||||
|
border-bottom: 2px solid #a7a8ff
|
||||||
|
|
||||||
|
display: flex;
|
||||||
|
flex-flow: row nowrap;
|
||||||
|
justify-content: flex-start;
|
||||||
|
align-items: center;
|
||||||
|
align-content: stretch;
|
||||||
|
|
||||||
|
div, span, i
|
||||||
|
//display: inline-block
|
||||||
|
flex: 0 0 auto
|
||||||
|
margin-right: 4px
|
||||||
|
.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
|
||||||
input[name='productFilter']
|
input[name='productFilter']
|
||||||
font-size: 1.2em
|
font-size: 1.2em
|
||||||
display: inline
|
display: inline
|
||||||
width: auto
|
width: auto
|
||||||
.showAlternateNames
|
.showAlternateNames
|
||||||
margin-right: 20px
|
margin-right: 20px
|
||||||
|
.heading
|
||||||
|
display: inline-block
|
||||||
|
.name
|
||||||
|
padding: 6px 10px
|
||||||
|
margin-bottom: 6px
|
||||||
|
border-radius: 10px
|
||||||
|
font-size: 1.4em
|
||||||
|
text-transform: uppercase
|
||||||
|
font-weight: 800
|
||||||
|
background: #c1c2ff
|
||||||
|
.nameEditor
|
||||||
|
display: none
|
||||||
|
.clearFilter
|
||||||
|
font-size: 1.6em
|
||||||
.selectionProductsListing
|
.selectionProductsListing
|
||||||
width: 100%
|
width: 100%
|
||||||
.selectionProduct
|
.selectionProduct
|
||||||
color: #9f9f9f
|
color: #9f9f9f
|
||||||
font-size: 1.5em
|
font-size: 1.3em
|
||||||
width: 400px
|
width: 400px
|
||||||
.include, .includeAs
|
.include, .includeAs
|
||||||
text-overflow: ellipsis
|
text-overflow: ellipsis
|
||||||
@@ -25,26 +62,26 @@
|
|||||||
cursor: pointer
|
cursor: pointer
|
||||||
.includedRemove:hover, .includedAdd:hover
|
.includedRemove:hover, .includedAdd:hover
|
||||||
color: blue
|
color: blue
|
||||||
|
span.deactivated
|
||||||
|
background: #ff0
|
||||||
|
//-webkit-box-shadow: inset 28px 0 10px -10px #fff
|
||||||
|
//-moz-box-shadow: inset 28px 0 10px -10px #fff
|
||||||
|
//box-shadow: inset 28px 0 10px -10px #fff
|
||||||
|
box-shadow: inset 0px 0 100px 0px #fff
|
||||||
|
span.hidden
|
||||||
|
background: rgba(255, 20, 20, .7)
|
||||||
|
//-webkit-box-shadow: inset 28px 0 10px -10px #fff
|
||||||
|
//-moz-box-shadow: inset 28px 0 10px -10px #fff
|
||||||
|
box-shadow: inset 0px 0px 100px 0px #fff
|
||||||
.selectionProduct.selected
|
.selectionProduct.selected
|
||||||
color: black
|
color: black
|
||||||
.configurationControls
|
|
||||||
width: 100%
|
|
||||||
background: #c1c2ff
|
|
||||||
border-bottom: 2px solid #a7a8ff
|
|
||||||
.heading
|
|
||||||
.name
|
|
||||||
font-size: 1.5em
|
|
||||||
text-transform: uppercase
|
|
||||||
font-weight: 800
|
|
||||||
.nameEditor
|
|
||||||
display: none
|
|
||||||
.configurationProductsListing
|
.configurationProductsListing
|
||||||
width: 100%
|
width: 100%
|
||||||
.product
|
.product
|
||||||
width: 300px
|
width: 300px
|
||||||
.name
|
.name
|
||||||
color: #9f9f9f
|
color: #4a4a4a
|
||||||
font-size: 1.5em
|
font-size: 1.1em
|
||||||
margin-bottom: 6px
|
margin-bottom: 6px
|
||||||
text-overflow: ellipsis
|
text-overflow: ellipsis
|
||||||
white-space: nowrap
|
white-space: nowrap
|
||||||
|
|||||||
@@ -83,11 +83,20 @@ Template.SalesSheetEditorProductSelection.onCreated(function() {
|
|||||||
//Note: This is not reactive because we don't expect the sales sheet to change without closing the editor (re-editing would open a new template instance). Also, the sales sheet is a clone of the real one, so any changes will be lost if not saved.
|
//Note: This is not reactive because we don't expect the sales sheet to change without closing the editor (re-editing would open a new template instance). Also, the sales sheet is a clone of the real one, so any changes will be lost if not saved.
|
||||||
this.salesSheet = this.data.salesSheet;
|
this.salesSheet = this.data.salesSheet;
|
||||||
this.productNameFilter = new ReactiveVar("");
|
this.productNameFilter = new ReactiveVar("");
|
||||||
|
Session.set(PREFIX + "showHidden", false);
|
||||||
});
|
});
|
||||||
Template.SalesSheetEditorProductSelection.events({
|
Template.SalesSheetEditorProductSelection.events({
|
||||||
'keyup input[name="productFilter"]': _.throttle(function(event, template) {
|
'keyup input[name="productFilter"]': _.throttle(function(event, template) {
|
||||||
template.productNameFilter.set($(event.target).val());
|
template.productNameFilter.set($(event.target).val());
|
||||||
})
|
}),
|
||||||
|
'click .clearFilter': function(event, template) {
|
||||||
|
template.$('input[name="productFilter"]').val('');
|
||||||
|
template.productNameFilter.set('');
|
||||||
|
},
|
||||||
|
'change input[name="showHidden"]': function(event, template) {
|
||||||
|
//console.log("changed " + $(event.target).prop('checked'));
|
||||||
|
Session.set(PREFIX + "showHidden", $(event.target).prop('checked'));
|
||||||
|
}
|
||||||
});
|
});
|
||||||
Template.SalesSheetEditorProductSelection.helpers({
|
Template.SalesSheetEditorProductSelection.helpers({
|
||||||
products: function() {
|
products: function() {
|
||||||
@@ -95,7 +104,8 @@ Template.SalesSheetEditorProductSelection.helpers({
|
|||||||
let filter = Template.instance().productNameFilter.get();
|
let filter = Template.instance().productNameFilter.get();
|
||||||
let products = salesSheet.products ? salesSheet.products : [];
|
let products = salesSheet.products ? salesSheet.products : [];
|
||||||
let productMap = {};
|
let productMap = {};
|
||||||
let dbQuery;
|
let dbQuery = [];
|
||||||
|
let showHidden = Session.get(PREFIX + "showHidden");
|
||||||
|
|
||||||
//Map the products in the sales sheet by their id so we can later only add products to the list that are not on the sheet.
|
//Map the products in the sales sheet by their id so we can later only add products to the list that are not on the sheet.
|
||||||
for(next of products) {
|
for(next of products) {
|
||||||
@@ -115,9 +125,17 @@ Template.SalesSheetEditorProductSelection.helpers({
|
|||||||
}
|
}
|
||||||
|
|
||||||
regex += '.*';
|
regex += '.*';
|
||||||
dbQuery = {name: {$regex: regex, $options: 'i'}};
|
dbQuery.push({name: {$regex: regex, $options: 'i'}});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(!showHidden) {
|
||||||
|
//Ignore any hidden elements by showing those not hidden, or those without the hidden field.
|
||||||
|
dbQuery.push({$or: [{hidden: false}, {hidden: {$exists:false}}]});
|
||||||
|
}
|
||||||
|
|
||||||
|
if(dbQuery.length > 0) dbQuery = {$and: dbQuery};
|
||||||
else dbQuery = {};
|
else dbQuery = {};
|
||||||
|
|
||||||
let allProducts = Meteor.collections.Products.find(dbQuery).fetch();
|
let allProducts = Meteor.collections.Products.find(dbQuery).fetch();
|
||||||
|
|
||||||
//Mark all the products that are currently included in the sheet and note the name they are included as.
|
//Mark all the products that are currently included in the sheet and note the name they are included as.
|
||||||
@@ -145,18 +163,21 @@ Template.SalesSheetEditorProductSelectionRow.events({
|
|||||||
let sheet = template.parentTemplate(1).salesSheet;
|
let sheet = template.parentTemplate(1).salesSheet;
|
||||||
|
|
||||||
if(template.sheetProduct.get()) { //Remove the product from the sheet, or rename it (if the names don't match and the user clicked on the name instead of the checkbox).
|
if(template.sheetProduct.get()) { //Remove the product from the sheet, or rename it (if the names don't match and the user clicked on the name instead of the checkbox).
|
||||||
|
|
||||||
|
// TODO: The commented code fails to detect newly added (to the sheet) products and instead of removing them, it seems to be changing the name when unchecking. This should also keep the element checked in the list instead of unchecking.
|
||||||
|
|
||||||
//If the click was on the product's name, and the product name is different from the name used for it in the sheet, then use the product name as the name in the sheet instead of removing it from the sheet.
|
//If the click was on the product's name, and the product name is different from the name used for it in the sheet, then use the product name as the name in the sheet instead of removing it from the sheet.
|
||||||
if($(event.target).closest('.productName') && this.name != template.sheetProduct.get().name) {
|
//if($(event.target).closest('.productName') && this.name !== template.sheetProduct.get().name) {
|
||||||
template.sheetProduct.get().name = this.name;
|
// template.sheetProduct.get().name = this.name;
|
||||||
}
|
//}
|
||||||
else {
|
//else {
|
||||||
let index = sheet.products.indexOf(this.sheetProduct);
|
let index = sheet.products.indexOf(template.sheetProduct.get());
|
||||||
|
|
||||||
//Remove the product data from the sheet first. Template.parentData(1) is the sheet.
|
//Remove the product data from the sheet first. Template.parentData(1) is the sheet.
|
||||||
if(index >= 0) sheet.products.splice(index, 1);
|
if(index >= 0) sheet.products.splice(index, 1);
|
||||||
//Clear the sheet product data from the actual product.
|
//Clear the sheet product data from the actual product.
|
||||||
template.sheetProduct.set(undefined);
|
template.sheetProduct.set(undefined);
|
||||||
}
|
//}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
let sheetProduct = {name: this.name, productId: this._id, measureIds: this.measures.length > 2 ? this.measures.slice(0,2) : this.measures};
|
let sheetProduct = {name: this.name, productId: this._id, measureIds: this.measures.length > 2 ? this.measures.slice(0,2) : this.measures};
|
||||||
@@ -172,8 +193,19 @@ Template.SalesSheetEditorProductSelectionRow.helpers({
|
|||||||
sheetProduct: function() {
|
sheetProduct: function() {
|
||||||
return Template.instance().sheetProduct.get();
|
return Template.instance().sheetProduct.get();
|
||||||
},
|
},
|
||||||
|
hidden: function() {
|
||||||
|
return this.hidden;
|
||||||
|
},
|
||||||
|
deactivated: function() {
|
||||||
|
return this.deactivated;
|
||||||
|
},
|
||||||
sheetProductName: function() {
|
sheetProductName: function() {
|
||||||
return Template.instance().sheetProduct.get().name;
|
return Template.instance().sheetProduct.get().name;
|
||||||
|
},
|
||||||
|
showAlternateName: function() {
|
||||||
|
let sheetProduct = Template.instance().sheetProduct.get();
|
||||||
|
|
||||||
|
return sheetProduct && sheetProduct.name !== this.name;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -187,7 +219,7 @@ Template.SalesSheetEditorConfiguration.onCreated(function() {
|
|||||||
//Note: This is not reactive because we don't expect the sales sheet to change without closing the editor (re-editing would open a new template instance). Also, the sales sheet is a clone of the real one, so any changes will be lost if not saved.
|
//Note: This is not reactive because we don't expect the sales sheet to change without closing the editor (re-editing would open a new template instance). Also, the sales sheet is a clone of the real one, so any changes will be lost if not saved.
|
||||||
this.salesSheet = this.data.salesSheet;
|
this.salesSheet = this.data.salesSheet;
|
||||||
this.measures = new ReactiveDict();
|
this.measures = new ReactiveDict();
|
||||||
this.productsDependancy = new Tracker.Dependency;
|
this.productsDependency = new Tracker.Dependency;
|
||||||
|
|
||||||
Tracker.autorun(function() {
|
Tracker.autorun(function() {
|
||||||
let measures = Meteor.collections.Measures.find({}).fetch();
|
let measures = Meteor.collections.Measures.find({}).fetch();
|
||||||
@@ -197,12 +229,14 @@ Template.SalesSheetEditorConfiguration.onCreated(function() {
|
|||||||
template.measures.set(measure._id, measure);
|
template.measures.set(measure._id, measure);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
Session.set(PREFIX + "showMeasures", false);
|
||||||
});
|
});
|
||||||
Template.SalesSheetEditorConfiguration.onRendered(function() {
|
Template.SalesSheetEditorConfiguration.onRendered(function() {
|
||||||
let template = this;
|
let template = this;
|
||||||
|
|
||||||
//Setup the drag and drop for the view.
|
//Setup the drag and drop for the view.
|
||||||
this.drake = dragula([this.$('.configurationProductsListing')[0], this.$('.configurationControls')[0]], {
|
this.drake = dragula([this.$('.configurationProductsListing')[0], this.$('.tableControls')[0]], {
|
||||||
moves: function(el, container, handle, sibling) {
|
moves: function(el, container, handle, sibling) {
|
||||||
//Don't allow drag and drop of buttons - we want them to be clickable.
|
//Don't allow drag and drop of buttons - we want them to be clickable.
|
||||||
return !$(handle).hasClass("button");
|
return !$(handle).hasClass("button");
|
||||||
@@ -212,7 +246,7 @@ Template.SalesSheetEditorConfiguration.onRendered(function() {
|
|||||||
return (!sibling || !$(sibling).hasClass('newHeading'));
|
return (!sibling || !$(sibling).hasClass('newHeading'));
|
||||||
},
|
},
|
||||||
copy: function(el, source) {
|
copy: function(el, source) {
|
||||||
return $(el).hasClass('heading') && $(source).hasClass('configurationControls');
|
return $(el).hasClass('heading') && $(source).hasClass('tableControls');
|
||||||
},
|
},
|
||||||
ignoreInputTextSelection: true
|
ignoreInputTextSelection: true
|
||||||
}).on('drop', function(el, target, source, sibling) {
|
}).on('drop', function(el, target, source, sibling) {
|
||||||
@@ -225,7 +259,7 @@ Template.SalesSheetEditorConfiguration.onRendered(function() {
|
|||||||
//Remove the element that was just added by the D&D. The element will be re-added by the template in just a moment. We need the template to add the element so that events will be properly handled for it by meteor.
|
//Remove the element that was just added by the D&D. The element will be re-added by the template in just a moment. We need the template to add the element so that events will be properly handled for it by meteor.
|
||||||
el.parentNode.removeChild(el);
|
el.parentNode.removeChild(el);
|
||||||
//Notify the template engine that the products list has changed so it can be re-rendered.
|
//Notify the template engine that the products list has changed so it can be re-rendered.
|
||||||
template.productsDependancy.changed();
|
template.productsDependency.changed();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
@@ -257,6 +291,9 @@ Template.SalesSheetEditorConfiguration.onDestroyed(function() {
|
|||||||
this.drake.destroy();
|
this.drake.destroy();
|
||||||
});
|
});
|
||||||
Template.SalesSheetEditorConfiguration.events({
|
Template.SalesSheetEditorConfiguration.events({
|
||||||
|
'change input[name="showMeasures"]': function(event, template) {
|
||||||
|
Session.set(PREFIX + "showMeasures", $(event.target).prop('checked'));
|
||||||
|
}
|
||||||
});
|
});
|
||||||
Template.SalesSheetEditorConfiguration.helpers({
|
Template.SalesSheetEditorConfiguration.helpers({
|
||||||
products: function() {
|
products: function() {
|
||||||
@@ -264,7 +301,7 @@ Template.SalesSheetEditorConfiguration.helpers({
|
|||||||
let products = template.salesSheet.products;
|
let products = template.salesSheet.products;
|
||||||
|
|
||||||
//Mark this call as depending on the products array. When we change the array later, we will call changed() on the dependency and it will trigger this function (and the calling template setup) to be re-run.
|
//Mark this call as depending on the products array. When we change the array later, we will call changed() on the dependency and it will trigger this function (and the calling template setup) to be re-run.
|
||||||
template.productsDependancy.depend();
|
template.productsDependency.depend();
|
||||||
|
|
||||||
return products;
|
return products;
|
||||||
}
|
}
|
||||||
@@ -295,7 +332,7 @@ Template.SalesSheetEditorConfigurationRow.onCreated(function() {
|
|||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
template.parentTemplate(1).salesSheet.products.splice(index, 1);
|
template.parentTemplate(1).salesSheet.products.splice(index, 1);
|
||||||
template.parentTemplate(1).productsDependancy.changed();
|
template.parentTemplate(1).productsDependency.changed();
|
||||||
}
|
}
|
||||||
|
|
||||||
template.$('.heading .nameEditor, .heading .headingNameRow').removeClass('edit');
|
template.$('.heading .nameEditor, .heading .headingNameRow').removeClass('edit');
|
||||||
@@ -332,10 +369,13 @@ Template.SalesSheetEditorConfigurationRow.helpers({
|
|||||||
},
|
},
|
||||||
isProduct: function() {
|
isProduct: function() {
|
||||||
return !!this.productId;
|
return !!this.productId;
|
||||||
|
},
|
||||||
|
showMeasures: function() {
|
||||||
|
return Session.get(PREFIX + "showMeasures");
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
Template.SalesSheetEditorConfigurationRow.events({
|
Template.SalesSheetEditorConfigurationRow.events({
|
||||||
'click .heading .name': function(event, template) {
|
'dblclick .heading .name': function(event, template) {
|
||||||
template.$('.nameEditor, .headingNameRow').addClass('edit');
|
template.$('.nameEditor, .headingNameRow').addClass('edit');
|
||||||
template.$('input[name="name"]').select();
|
template.$('input[name="name"]').select();
|
||||||
},
|
},
|
||||||
@@ -360,7 +400,7 @@ Template.SalesSheetEditorConfigurationRow.events({
|
|||||||
'click .heading .reject': function(event, template) {
|
'click .heading .reject': function(event, template) {
|
||||||
template.handleHeaderEditorCancelAndClose();
|
template.handleHeaderEditorCancelAndClose();
|
||||||
},
|
},
|
||||||
'click .product .name': function(event, template) {
|
'dblclick .product .name': function(event, template) {
|
||||||
template.$('.nameEditor, .name').addClass('edit');
|
template.$('.nameEditor, .name').addClass('edit');
|
||||||
template.$('input[name="name"]').select();
|
template.$('input[name="name"]').select();
|
||||||
},
|
},
|
||||||
@@ -414,6 +454,6 @@ Template.SalesSheetEditorConfigurationRow.events({
|
|||||||
});
|
});
|
||||||
|
|
||||||
//Notify anything depending on the products list that they have been modified.
|
//Notify anything depending on the products list that they have been modified.
|
||||||
template.parentTemplate(1).productsDependancy.changed();
|
template.parentTemplate(1).productsDependency.changed();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -208,7 +208,6 @@ Template.SalesSheetFormProduct.onCreated(function() {
|
|||||||
this.product = new ReactiveVar(); //The actual product for this sheet product.
|
this.product = new ReactiveVar(); //The actual product for this sheet product.
|
||||||
this.total = new ReactiveVar(0);
|
this.total = new ReactiveVar(0);
|
||||||
|
|
||||||
|
|
||||||
this.reset = function() {
|
this.reset = function() {
|
||||||
for(let measureTemplate of template.measureTemplates) {
|
for(let measureTemplate of template.measureTemplates) {
|
||||||
measureTemplate.reset();
|
measureTemplate.reset();
|
||||||
|
|||||||
27
imports/ui/TestList.html
Normal file
27
imports/ui/TestList.html
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
<template name="TestList">
|
||||||
|
<div id="testList">
|
||||||
|
<div id="sortAsc" class="clickable">Sort ASC</div>
|
||||||
|
<div id="sortDesc" class="clickable">Sort DESC</div>
|
||||||
|
<div class="testListListing columnContainer">
|
||||||
|
{{#each products}}
|
||||||
|
{{>TestListRow}}
|
||||||
|
{{/each}}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<template name="TestListRow">
|
||||||
|
<div class="selectionProduct {{#if sheetProduct}}selected{{/if}} columnContent">
|
||||||
|
<span class="include clickable">
|
||||||
|
{{#if sheetProduct}}
|
||||||
|
<i class="fa fa-check-circle" aria-hidden="true"></i>
|
||||||
|
{{else}}
|
||||||
|
<i class="fa fa-circle-o" aria-hidden="true"></i>
|
||||||
|
{{/if}}
|
||||||
|
<span class="productName noselect">{{name}}</span>
|
||||||
|
</span>
|
||||||
|
{{#if showAlternateName}}
|
||||||
|
<div class="includeAs"> as "{{sheetProductName}}"</div>
|
||||||
|
{{/if}}
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
4
imports/ui/TestList.import.styl
vendored
Normal file
4
imports/ui/TestList.import.styl
vendored
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
#testList
|
||||||
|
margin: 10px 20px
|
||||||
|
height: 100%
|
||||||
|
|
||||||
266
imports/ui/TestList.js
Normal file
266
imports/ui/TestList.js
Normal file
@@ -0,0 +1,266 @@
|
|||||||
|
|
||||||
|
import './TestList.html';
|
||||||
|
import swal from 'sweetalert2';
|
||||||
|
import dragula from 'dragula';
|
||||||
|
|
||||||
|
//******************************************************************
|
||||||
|
//** Tests a list of objects held by an object, where the list is sortable and dragable.
|
||||||
|
//** The objects are not in a repository (not in Mongo).
|
||||||
|
//**
|
||||||
|
//** The specific domain is a `SalesSheet` which holds a list of `Product` objects which each have a `name` property.
|
||||||
|
//******************************************************************
|
||||||
|
|
||||||
|
Template.TestList.onCreated(function() {
|
||||||
|
let template = this;
|
||||||
|
//Here, this is the template, and this.data is an object containing the 'parentTemplate' and 'salesSheet' properties.
|
||||||
|
//Save the sales sheet as a property of this template to make the code later easier to read.
|
||||||
|
//Note: This is not reactive because we don't expect the sales sheet to change without closing the editor (re-editing would open a new template instance). Also, the sales sheet is a clone of the real one, so any changes will be lost if not saved.
|
||||||
|
this.salesSheet = {products: [{name: "A Product", productId: 1}, {name: "E Product", productId: 2}, {name: "B Product", productId: 3}, {name: "G Product", productId: 4}, {name: "H Product", productId: 5}, {name: "I Product", productId: 6}, {name: "C Product", productId: 7}, {name: "D Product", productId: 8}, {name: "F Product", productId: 9}]};
|
||||||
|
this.productsDependency = new Tracker.Dependency;
|
||||||
|
|
||||||
|
this.sort = function(sortAlphabetical) {
|
||||||
|
let firstIndex = 0;
|
||||||
|
let products = template.salesSheet.products;
|
||||||
|
let length = 0;
|
||||||
|
|
||||||
|
while(firstIndex + length < products.length && products[firstIndex + length].productId) {
|
||||||
|
length++;
|
||||||
|
}
|
||||||
|
|
||||||
|
//Sort the part of the array that contains products under the sorted heading.
|
||||||
|
products.partialSort(firstIndex, length, function(a, b) {
|
||||||
|
return sortAlphabetical ? (a.name < b.name ? -1 : 1) : (a.name > b.name ? -1 : 1);
|
||||||
|
});
|
||||||
|
|
||||||
|
//Notify anything depending on the products list that they have been modified.
|
||||||
|
template.productsDependency.changed();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
Template.TestList.onRendered(function() {
|
||||||
|
let template = this;
|
||||||
|
|
||||||
|
//Setup the drag and drop for the view.
|
||||||
|
this.drake = dragula([this.$('.configurationProductsListing')[0], this.$('.configurationControls')[0]], {
|
||||||
|
moves: function(el, container, handle, sibling) {
|
||||||
|
//Don't allow drag and drop of buttons - we want them to be clickable.
|
||||||
|
return !$(handle).hasClass("button");
|
||||||
|
},
|
||||||
|
//Checks whether the element `el` can be moved from the container `target`, to the container `source`, above the `sibling` element.
|
||||||
|
accepts: function(el, target, source, sibling) {
|
||||||
|
return (!sibling || !$(sibling).hasClass('newHeading'));
|
||||||
|
},
|
||||||
|
copy: function(el, source) {
|
||||||
|
return $(el).hasClass('heading') && $(source).hasClass('configurationControls');
|
||||||
|
},
|
||||||
|
ignoreInputTextSelection: true
|
||||||
|
}).on('drop', function(el, target, source, sibling) {
|
||||||
|
if($(el).hasClass('heading')) {
|
||||||
|
if(el.parentNode) {
|
||||||
|
let array = template.salesSheet.products;
|
||||||
|
|
||||||
|
//Add the heading to the product array.
|
||||||
|
array.add({name: "New Heading"}, $(el).index());
|
||||||
|
//Remove the element that was just added by the D&D. The element will be re-added by the template in just a moment. We need the template to add the element so that events will be properly handled for it by meteor.
|
||||||
|
el.parentNode.removeChild(el);
|
||||||
|
//Notify the template engine that the products list has changed so it can be re-rendered.
|
||||||
|
template.productsDependency.changed();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
//Get the item from the DOM using the blaze data structure. We could make this more blaze agnostic by attaching the object as data to the DOM in the view, but we really can't escape blaze, so why bother.
|
||||||
|
//let item = el.$blaze_range.view._templateInstance.data;
|
||||||
|
let productId = $(el).data('model');
|
||||||
|
let array = template.salesSheet.products;
|
||||||
|
let item = undefined;
|
||||||
|
|
||||||
|
for(let product of array) {
|
||||||
|
if(productId === product.productId) {
|
||||||
|
item = product;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(item) {
|
||||||
|
//Rearrange the array of products on the sheet.
|
||||||
|
array.move(array.indexOf(item), $(el).index());
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
console.log("ERROR: Unable to locate the moved item.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
Template.TestList.onDestroyed(function() {
|
||||||
|
//Clean up after the drag and drop.
|
||||||
|
this.drake.destroy();
|
||||||
|
});
|
||||||
|
Template.TestList.events({
|
||||||
|
'click #sortAsc': function(event, template) {
|
||||||
|
Template.instance().sort(true);
|
||||||
|
},
|
||||||
|
'click #sortDesc': function(event, template) {
|
||||||
|
Template.instance().sort(false);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
Template.TestList.helpers({
|
||||||
|
products: function() {
|
||||||
|
let template = Template.instance();
|
||||||
|
let products = template.salesSheet.products;
|
||||||
|
|
||||||
|
//Mark this call as depending on the products array. When we change the array later, we will call changed() on the dependency and it will trigger this function (and the calling template setup) to be re-run.
|
||||||
|
template.productsDependency.depend();
|
||||||
|
|
||||||
|
return products;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
//Note: The data to this template is a product metadata object that is part of a sheet and wrappers (by ID association) a product in the system. See the schema in SalesSheet.js, look for 'products.$' to see the type definition for this data.
|
||||||
|
Template.TestListRow.onCreated(function() {
|
||||||
|
let template = this;
|
||||||
|
|
||||||
|
this.handleHeaderEditorCancelAndClose = function() {
|
||||||
|
let $inputField = template.$("input[name='name']");
|
||||||
|
let index = template.$('.heading').index();
|
||||||
|
|
||||||
|
//Reset the text field.
|
||||||
|
$inputField.val(template.parentTemplate(1).salesSheet.products[index].name);
|
||||||
|
template.$('.heading .nameEditor, .heading .headingNameRow').removeClass('edit');
|
||||||
|
};
|
||||||
|
this.handleHeaderEditorApplyAndClose = function() {
|
||||||
|
let $inputField = template.$("input[name='name']");
|
||||||
|
let name = $inputField.val();
|
||||||
|
let index = template.$('.heading').index();
|
||||||
|
|
||||||
|
if(name) name = name.trim();
|
||||||
|
|
||||||
|
if(name && name.length > 0) {
|
||||||
|
template.parentTemplate(1).salesSheet.products[index].name = name;
|
||||||
|
template.$('.heading .name').text(name);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
template.parentTemplate(1).salesSheet.products.splice(index, 1);
|
||||||
|
template.parentTemplate(1).productsDependency.changed();
|
||||||
|
}
|
||||||
|
|
||||||
|
template.$('.heading .nameEditor, .heading .headingNameRow').removeClass('edit');
|
||||||
|
};
|
||||||
|
this.handleProductEditorCancelAndClose = function() {
|
||||||
|
let $inputField = template.$("input[name='name']");
|
||||||
|
let index = template.$('.product').index();
|
||||||
|
|
||||||
|
//Reset the text field.
|
||||||
|
$inputField.val(template.parentTemplate(1).salesSheet.products[index].name);
|
||||||
|
template.$('.product .nameEditor, .product .name').removeClass('edit');
|
||||||
|
};
|
||||||
|
this.handleProductEditorApplyAndClose = function() {
|
||||||
|
let $inputField = template.$("input[name='name']");
|
||||||
|
let name = $inputField.val();
|
||||||
|
let index = template.$('.product').index();
|
||||||
|
|
||||||
|
template.parentTemplate(1).salesSheet.products[index].name = name;
|
||||||
|
template.$('.product .name').text(name);
|
||||||
|
template.$('.product .nameEditor, .product .name').removeClass('edit');
|
||||||
|
};
|
||||||
|
});
|
||||||
|
Template.TestListRow.helpers({
|
||||||
|
measureName: function(measureId) {
|
||||||
|
return Template.instance().parentTemplate(1).measures.get(measureId).name;
|
||||||
|
},
|
||||||
|
measures: function() {
|
||||||
|
let product = Meteor.collections.Products.findOne(this.productId);
|
||||||
|
|
||||||
|
return product.measures;
|
||||||
|
},
|
||||||
|
isSelected: function(measureId) {
|
||||||
|
return this.measureIds.includes(measureId);
|
||||||
|
},
|
||||||
|
isProduct: function() {
|
||||||
|
return !!this.productId;
|
||||||
|
},
|
||||||
|
showMeasures: function() {
|
||||||
|
return Session.get(PREFIX + "showMeasures");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
Template.TestListRow.events({
|
||||||
|
'dblclick .heading .name': function(event, template) {
|
||||||
|
template.$('.nameEditor, .headingNameRow').addClass('edit');
|
||||||
|
template.$('input[name="name"]').select();
|
||||||
|
},
|
||||||
|
'blur .heading input[name="name"]': function(event, template) {
|
||||||
|
template.handleHeaderEditorApplyAndClose();
|
||||||
|
},
|
||||||
|
'keyup .heading input[name="name"]': function(event, template) {
|
||||||
|
if(event.which === 13 || event.which === 9) { //Enter or Tab
|
||||||
|
template.handleHeaderEditorApplyAndClose();
|
||||||
|
event.stopPropagation();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
else if(event.which === 27) { //Escape
|
||||||
|
template.handleHeaderEditorCancelAndClose();
|
||||||
|
event.stopPropagation();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
'click .heading .accept': function(event, template) {
|
||||||
|
template.handleHeaderEditorApplyAndClose();
|
||||||
|
},
|
||||||
|
'click .heading .reject': function(event, template) {
|
||||||
|
template.handleHeaderEditorCancelAndClose();
|
||||||
|
},
|
||||||
|
'dblclick .product .name': function(event, template) {
|
||||||
|
template.$('.nameEditor, .name').addClass('edit');
|
||||||
|
template.$('input[name="name"]').select();
|
||||||
|
},
|
||||||
|
'blur .product input[name="name"]': function(event, template) {
|
||||||
|
template.handleProductEditorApplyAndClose();
|
||||||
|
},
|
||||||
|
'keyup .product input[name="name"]': function(event, template) {
|
||||||
|
if(event.which === 13 || event.which === 9) { //Enter or Tab
|
||||||
|
template.handleProductEditorApplyAndClose();
|
||||||
|
event.stopPropagation();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
else if(event.which === 27) { //Escape
|
||||||
|
template.handleProductEditorCancelAndClose();
|
||||||
|
event.stopPropagation();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
'click .product .accept': function(event, template) {
|
||||||
|
template.handleProductEditorApplyAndClose();
|
||||||
|
},
|
||||||
|
'click .product .reject': function(event, template) {
|
||||||
|
template.handleProductEditorCancelAndClose();
|
||||||
|
},
|
||||||
|
'click .measureButton': function(event, template) {
|
||||||
|
let measureId = $(event.target).data("model");
|
||||||
|
|
||||||
|
$(event.target).toggleClass("selected");
|
||||||
|
|
||||||
|
if(this.measureIds.includes(measureId))
|
||||||
|
this.measureIds.remove(measureId);
|
||||||
|
else
|
||||||
|
this.measureIds.add(measureId);
|
||||||
|
},
|
||||||
|
'click .heading .sort': function(event, template) {
|
||||||
|
let width = event.currentTarget.offsetWidth;
|
||||||
|
let x = event.pageX - event.currentTarget.offsetLeft;
|
||||||
|
let sortAlphabetical = x <= (width / 2);
|
||||||
|
let headingIndex = template.$(event.target).closest(".heading").index();
|
||||||
|
let firstIndex = headingIndex + 1;
|
||||||
|
let products = template.parentTemplate(1).salesSheet.products;
|
||||||
|
let length = 0;
|
||||||
|
|
||||||
|
while(firstIndex + length < products.length && products[firstIndex + length].productId) {
|
||||||
|
length++;
|
||||||
|
}
|
||||||
|
|
||||||
|
//Sort the part of the array that contains products under the sorted heading.
|
||||||
|
products.partialSort(firstIndex, length, function(a, b) {
|
||||||
|
return sortAlphabetical ? (a.name < b.name ? -1 : 1) : (a.name > b.name ? -1 : 1);
|
||||||
|
});
|
||||||
|
|
||||||
|
//Notify anything depending on the products list that they have been modified.
|
||||||
|
template.parentTemplate(1).productsDependency.changed();
|
||||||
|
}
|
||||||
|
});
|
||||||
2
imports/ui/layouts/Body.import.styl
vendored
2
imports/ui/layouts/Body.import.styl
vendored
@@ -80,7 +80,7 @@
|
|||||||
display: block
|
display: block
|
||||||
a
|
a
|
||||||
color: black
|
color: black
|
||||||
padding: 15px 20px
|
padding: 10px 20px
|
||||||
cursor: pointer
|
cursor: pointer
|
||||||
text-decoration: none
|
text-decoration: none
|
||||||
display: block
|
display: block
|
||||||
|
|||||||
@@ -100,7 +100,7 @@ if(!Array.prototype.partialSort) {
|
|||||||
sorted.sort(compareFunction);
|
sorted.sort(compareFunction);
|
||||||
|
|
||||||
//Put the sorted array elements back into the array.
|
//Put the sorted array elements back into the array.
|
||||||
for(let i = index, j = 0; i <= length; i++, j++) {
|
for(let i = index, j = 0; j < length; i++, j++) {
|
||||||
this[i] = sorted[j];
|
this[i] = sorted[j];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,4 +6,35 @@ Date.prototype.toDateInputValue = (function() {
|
|||||||
let local = new Date(this);
|
let local = new Date(this);
|
||||||
local.setMinutes(this.getMinutes() - this.getTimezoneOffset());
|
local.setMinutes(this.getMinutes() - this.getTimezoneOffset());
|
||||||
return local.toJSON().slice(0,10);
|
return local.toJSON().slice(0,10);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
Date.prototype.getWeek = function() {
|
||||||
|
let dowOffset = 1; // I am fixing this to indicate that the first day of the week is always Monday (weeks end on Sunday), for this application. This was a parameter in the original code.
|
||||||
|
|
||||||
|
//dowOffset = typeof(dowOffset) == 'number' ? dowOffset : 0; //default dowOffset to zero - This should check to ensure that dowOffset is between 0..6
|
||||||
|
let newYear = new Date(this.getFullYear(),0,1);
|
||||||
|
let day = newYear.getDay() - dowOffset; //the day of week the year begins on
|
||||||
|
day = (day >= 0 ? day : day + 7);
|
||||||
|
// The number of days from the beginning of the year to this.day
|
||||||
|
let daynum = Math.floor((this.getTime() - newYear.getTime() - (this.getTimezoneOffset()-newYear.getTimezoneOffset())*60000)/86400000) + 1;
|
||||||
|
let weeknum;
|
||||||
|
|
||||||
|
// I have removed the mid-week starting cutoff detection because in this app we always want to start with week #1 (never have a week zero).
|
||||||
|
//if(day < 4) { //if the year starts before the middle of a week
|
||||||
|
weeknum = Math.floor((daynum + day - 1) / 7) + 1;
|
||||||
|
|
||||||
|
// I have turned off the detection of whether the last days of the year belong to this year's last week or next year's first week. This gets too confusing and does not result in any additional usefulness.
|
||||||
|
//if(weeknum > 52) {
|
||||||
|
// nYear = new Date(this.getFullYear() + 1, 0, 1);
|
||||||
|
// nday = nYear.getDay() - dowOffset;
|
||||||
|
// nday = nday >= 0 ? nday : nday + 7;
|
||||||
|
// // if the next year starts before the middle of the week, it is week #1 of that year
|
||||||
|
// weeknum = nday < 4 ? 1 : 53;
|
||||||
|
//}
|
||||||
|
//}
|
||||||
|
//else {
|
||||||
|
// weeknum = Math.floor((daynum+day-1)/7);
|
||||||
|
//}
|
||||||
|
|
||||||
|
return weeknum;
|
||||||
|
};
|
||||||
7
imports/util/scrollWidth.js
Normal file
7
imports/util/scrollWidth.js
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
var scrollBarWidth = getScrollBarWidth();
|
||||||
|
function getScrollBarWidth() {
|
||||||
|
let $outer = $('<div>').css({visibility: 'hidden', width: 100, overflow: 'scroll'}).appendTo('body'),
|
||||||
|
widthWithScroll = $('<div>').css({width: '100%'}).appendTo($outer).outerWidth();
|
||||||
|
$outer.remove();
|
||||||
|
return 100 - widthWithScroll;
|
||||||
|
}
|
||||||
@@ -12,9 +12,11 @@
|
|||||||
"d3": "^4.4.2",
|
"d3": "^4.4.2",
|
||||||
"dragula": "^3.7.2",
|
"dragula": "^3.7.2",
|
||||||
"jquery": "^3.1.1",
|
"jquery": "^3.1.1",
|
||||||
|
"jquery-mousewheel": "^3.1.13",
|
||||||
|
"malihu-custom-scrollbar-plugin": "latest",
|
||||||
"meteor-node-stubs": "^0.2.4",
|
"meteor-node-stubs": "^0.2.4",
|
||||||
"properties-reader": "0.0.15",
|
"properties-reader": "0.0.15",
|
||||||
"simpl-schema": "0.0.3",
|
"simpl-schema": "latest",
|
||||||
"sweetalert2": "^6.3.8"
|
"sweetalert2": "^6.3.8"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
0
packages/jquery-custom-scrollbar/README.md
Normal file
0
packages/jquery-custom-scrollbar/README.md
Normal file
11
packages/jquery-custom-scrollbar/jquery-custom-scrollbar-tests.js
vendored
Normal file
11
packages/jquery-custom-scrollbar/jquery-custom-scrollbar-tests.js
vendored
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
// Import Tinytest from the tinytest Meteor package.
|
||||||
|
import { Tinytest } from "meteor/tinytest";
|
||||||
|
|
||||||
|
// Import and rename a variable exported by jquery-custom-scrollbar.js.
|
||||||
|
import { name as packageName } from "meteor/wcrisman:jquery-custom-scrollbar";
|
||||||
|
|
||||||
|
// Write your tests here!
|
||||||
|
// Here is an example.
|
||||||
|
Tinytest.add('jquery-custom-scrollbar - example', function (test) {
|
||||||
|
test.equal(packageName, "jquery-custom-scrollbar");
|
||||||
|
});
|
||||||
7
packages/jquery-custom-scrollbar/jquery-custom-scrollbar.js
vendored
Normal file
7
packages/jquery-custom-scrollbar/jquery-custom-scrollbar.js
vendored
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
import 'jquery-mousewheel';
|
||||||
|
import "malihu-custom-scrollbar-plugin";
|
||||||
|
// Write your package code here!
|
||||||
|
|
||||||
|
// Variables exported by this module can be imported by other packages and
|
||||||
|
// applications. See jquery-custom-scrollbar-tests.js for an example of importing.
|
||||||
|
export const name = 'jquery-custom-scrollbar';
|
||||||
1267
packages/jquery-custom-scrollbar/jquery.mCustomScrollbar.css
Normal file
1267
packages/jquery-custom-scrollbar/jquery.mCustomScrollbar.css
Normal file
File diff suppressed because it is too large
Load Diff
2458
packages/jquery-custom-scrollbar/jquery.mCustomScrollbar.js
Normal file
2458
packages/jquery-custom-scrollbar/jquery.mCustomScrollbar.js
Normal file
File diff suppressed because it is too large
Load Diff
221
packages/jquery-custom-scrollbar/jquery.mousewheel.js
Normal file
221
packages/jquery-custom-scrollbar/jquery.mousewheel.js
Normal file
@@ -0,0 +1,221 @@
|
|||||||
|
/*!
|
||||||
|
* jQuery Mousewheel 3.1.13
|
||||||
|
*
|
||||||
|
* Copyright jQuery Foundation and other contributors
|
||||||
|
* Released under the MIT license
|
||||||
|
* http://jquery.org/license
|
||||||
|
*/
|
||||||
|
|
||||||
|
(function (factory) {
|
||||||
|
if ( typeof define === 'function' && define.amd ) {
|
||||||
|
// AMD. Register as an anonymous module.
|
||||||
|
define(['jquery'], factory);
|
||||||
|
} else if (typeof exports === 'object') {
|
||||||
|
// Node/CommonJS style for Browserify
|
||||||
|
module.exports = factory;
|
||||||
|
} else {
|
||||||
|
// Browser globals
|
||||||
|
factory(jQuery);
|
||||||
|
}
|
||||||
|
}(function ($) {
|
||||||
|
|
||||||
|
var toFix = ['wheel', 'mousewheel', 'DOMMouseScroll', 'MozMousePixelScroll'],
|
||||||
|
toBind = ( 'onwheel' in document || document.documentMode >= 9 ) ?
|
||||||
|
['wheel'] : ['mousewheel', 'DomMouseScroll', 'MozMousePixelScroll'],
|
||||||
|
slice = Array.prototype.slice,
|
||||||
|
nullLowestDeltaTimeout, lowestDelta;
|
||||||
|
|
||||||
|
if ( $.event.fixHooks ) {
|
||||||
|
for ( var i = toFix.length; i; ) {
|
||||||
|
$.event.fixHooks[ toFix[--i] ] = $.event.mouseHooks;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var special = $.event.special.mousewheel = {
|
||||||
|
version: '3.1.12',
|
||||||
|
|
||||||
|
setup: function() {
|
||||||
|
if ( this.addEventListener ) {
|
||||||
|
for ( var i = toBind.length; i; ) {
|
||||||
|
this.addEventListener( toBind[--i], handler, false );
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
this.onmousewheel = handler;
|
||||||
|
}
|
||||||
|
// Store the line height and page height for this particular element
|
||||||
|
$.data(this, 'mousewheel-line-height', special.getLineHeight(this));
|
||||||
|
$.data(this, 'mousewheel-page-height', special.getPageHeight(this));
|
||||||
|
},
|
||||||
|
|
||||||
|
teardown: function() {
|
||||||
|
if ( this.removeEventListener ) {
|
||||||
|
for ( var i = toBind.length; i; ) {
|
||||||
|
this.removeEventListener( toBind[--i], handler, false );
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
this.onmousewheel = null;
|
||||||
|
}
|
||||||
|
// Clean up the data we added to the element
|
||||||
|
$.removeData(this, 'mousewheel-line-height');
|
||||||
|
$.removeData(this, 'mousewheel-page-height');
|
||||||
|
},
|
||||||
|
|
||||||
|
getLineHeight: function(elem) {
|
||||||
|
var $elem = $(elem),
|
||||||
|
$parent = $elem['offsetParent' in $.fn ? 'offsetParent' : 'parent']();
|
||||||
|
if (!$parent.length) {
|
||||||
|
$parent = $('body');
|
||||||
|
}
|
||||||
|
return parseInt($parent.css('fontSize'), 10) || parseInt($elem.css('fontSize'), 10) || 16;
|
||||||
|
},
|
||||||
|
|
||||||
|
getPageHeight: function(elem) {
|
||||||
|
return $(elem).height();
|
||||||
|
},
|
||||||
|
|
||||||
|
settings: {
|
||||||
|
adjustOldDeltas: true, // see shouldAdjustOldDeltas() below
|
||||||
|
normalizeOffset: true // calls getBoundingClientRect for each event
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
$.fn.extend({
|
||||||
|
mousewheel: function(fn) {
|
||||||
|
return fn ? this.bind('mousewheel', fn) : this.trigger('mousewheel');
|
||||||
|
},
|
||||||
|
|
||||||
|
unmousewheel: function(fn) {
|
||||||
|
return this.unbind('mousewheel', fn);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
function handler(event) {
|
||||||
|
var orgEvent = event || window.event,
|
||||||
|
args = slice.call(arguments, 1),
|
||||||
|
delta = 0,
|
||||||
|
deltaX = 0,
|
||||||
|
deltaY = 0,
|
||||||
|
absDelta = 0,
|
||||||
|
offsetX = 0,
|
||||||
|
offsetY = 0;
|
||||||
|
event = $.event.fix(orgEvent);
|
||||||
|
event.type = 'mousewheel';
|
||||||
|
|
||||||
|
// Old school scrollwheel delta
|
||||||
|
if ( 'detail' in orgEvent ) { deltaY = orgEvent.detail * -1; }
|
||||||
|
if ( 'wheelDelta' in orgEvent ) { deltaY = orgEvent.wheelDelta; }
|
||||||
|
if ( 'wheelDeltaY' in orgEvent ) { deltaY = orgEvent.wheelDeltaY; }
|
||||||
|
if ( 'wheelDeltaX' in orgEvent ) { deltaX = orgEvent.wheelDeltaX * -1; }
|
||||||
|
|
||||||
|
// Firefox < 17 horizontal scrolling related to DOMMouseScroll event
|
||||||
|
if ( 'axis' in orgEvent && orgEvent.axis === orgEvent.HORIZONTAL_AXIS ) {
|
||||||
|
deltaX = deltaY * -1;
|
||||||
|
deltaY = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set delta to be deltaY or deltaX if deltaY is 0 for backwards compatabilitiy
|
||||||
|
delta = deltaY === 0 ? deltaX : deltaY;
|
||||||
|
|
||||||
|
// New school wheel delta (wheel event)
|
||||||
|
if ( 'deltaY' in orgEvent ) {
|
||||||
|
deltaY = orgEvent.deltaY * -1;
|
||||||
|
delta = deltaY;
|
||||||
|
}
|
||||||
|
if ( 'deltaX' in orgEvent ) {
|
||||||
|
deltaX = orgEvent.deltaX;
|
||||||
|
if ( deltaY === 0 ) { delta = deltaX * -1; }
|
||||||
|
}
|
||||||
|
|
||||||
|
// No change actually happened, no reason to go any further
|
||||||
|
if ( deltaY === 0 && deltaX === 0 ) { return; }
|
||||||
|
|
||||||
|
// Need to convert lines and pages to pixels if we aren't already in pixels
|
||||||
|
// There are three delta modes:
|
||||||
|
// * deltaMode 0 is by pixels, nothing to do
|
||||||
|
// * deltaMode 1 is by lines
|
||||||
|
// * deltaMode 2 is by pages
|
||||||
|
if ( orgEvent.deltaMode === 1 ) {
|
||||||
|
var lineHeight = $.data(this, 'mousewheel-line-height');
|
||||||
|
delta *= lineHeight;
|
||||||
|
deltaY *= lineHeight;
|
||||||
|
deltaX *= lineHeight;
|
||||||
|
} else if ( orgEvent.deltaMode === 2 ) {
|
||||||
|
var pageHeight = $.data(this, 'mousewheel-page-height');
|
||||||
|
delta *= pageHeight;
|
||||||
|
deltaY *= pageHeight;
|
||||||
|
deltaX *= pageHeight;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Store lowest absolute delta to normalize the delta values
|
||||||
|
absDelta = Math.max( Math.abs(deltaY), Math.abs(deltaX) );
|
||||||
|
|
||||||
|
if ( !lowestDelta || absDelta < lowestDelta ) {
|
||||||
|
lowestDelta = absDelta;
|
||||||
|
|
||||||
|
// Adjust older deltas if necessary
|
||||||
|
if ( shouldAdjustOldDeltas(orgEvent, absDelta) ) {
|
||||||
|
lowestDelta /= 40;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Adjust older deltas if necessary
|
||||||
|
if ( shouldAdjustOldDeltas(orgEvent, absDelta) ) {
|
||||||
|
// Divide all the things by 40!
|
||||||
|
delta /= 40;
|
||||||
|
deltaX /= 40;
|
||||||
|
deltaY /= 40;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get a whole, normalized value for the deltas
|
||||||
|
delta = Math[ delta >= 1 ? 'floor' : 'ceil' ](delta / lowestDelta);
|
||||||
|
deltaX = Math[ deltaX >= 1 ? 'floor' : 'ceil' ](deltaX / lowestDelta);
|
||||||
|
deltaY = Math[ deltaY >= 1 ? 'floor' : 'ceil' ](deltaY / lowestDelta);
|
||||||
|
|
||||||
|
// Normalise offsetX and offsetY properties
|
||||||
|
if ( special.settings.normalizeOffset && this.getBoundingClientRect ) {
|
||||||
|
var boundingRect = this.getBoundingClientRect();
|
||||||
|
offsetX = event.clientX - boundingRect.left;
|
||||||
|
offsetY = event.clientY - boundingRect.top;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add information to the event object
|
||||||
|
event.deltaX = deltaX;
|
||||||
|
event.deltaY = deltaY;
|
||||||
|
event.deltaFactor = lowestDelta;
|
||||||
|
event.offsetX = offsetX;
|
||||||
|
event.offsetY = offsetY;
|
||||||
|
// Go ahead and set deltaMode to 0 since we converted to pixels
|
||||||
|
// Although this is a little odd since we overwrite the deltaX/Y
|
||||||
|
// properties with normalized deltas.
|
||||||
|
event.deltaMode = 0;
|
||||||
|
|
||||||
|
// Add event and delta to the front of the arguments
|
||||||
|
args.unshift(event, delta, deltaX, deltaY);
|
||||||
|
|
||||||
|
// Clearout lowestDelta after sometime to better
|
||||||
|
// handle multiple device types that give different
|
||||||
|
// a different lowestDelta
|
||||||
|
// Ex: trackpad = 3 and mouse wheel = 120
|
||||||
|
if (nullLowestDeltaTimeout) { clearTimeout(nullLowestDeltaTimeout); }
|
||||||
|
nullLowestDeltaTimeout = setTimeout(nullLowestDelta, 200);
|
||||||
|
|
||||||
|
return ($.event.dispatch || $.event.handle).apply(this, args);
|
||||||
|
}
|
||||||
|
|
||||||
|
function nullLowestDelta() {
|
||||||
|
lowestDelta = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
function shouldAdjustOldDeltas(orgEvent, absDelta) {
|
||||||
|
// If this is an older event and the delta is divisable by 120,
|
||||||
|
// then we are assuming that the browser is treating this as an
|
||||||
|
// older mouse wheel event and that we should divide the deltas
|
||||||
|
// by 40 to try and get a more usable deltaFactor.
|
||||||
|
// Side note, this actually impacts the reported scroll distance
|
||||||
|
// in older browsers and can cause scrolling to be slower than native.
|
||||||
|
// Turn this off by setting $.event.special.mousewheel.settings.adjustOldDeltas to false.
|
||||||
|
return special.settings.adjustOldDeltas && orgEvent.type === 'mousewheel' && absDelta % 120 === 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
}));
|
||||||
29
packages/jquery-custom-scrollbar/package.js
vendored
Normal file
29
packages/jquery-custom-scrollbar/package.js
vendored
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
Package.describe({
|
||||||
|
name: 'wcrisman:jquery-custom-scrollbar',
|
||||||
|
version: '3.0.0',
|
||||||
|
// Brief, one-line summary of the package.
|
||||||
|
summary: 'Simple wrapper for the jquery-mousewheel & malihu-custom-scrollbar-plugin NPM packages. Users must install the version they want of these two NPM packages for this to work.',
|
||||||
|
// URL to the Git repository containing the source code for this package.
|
||||||
|
git: '',
|
||||||
|
// By default, Meteor will default to using README.md for documentation.
|
||||||
|
// To avoid submitting documentation, set this field to null.
|
||||||
|
documentation: 'README.md'
|
||||||
|
});
|
||||||
|
|
||||||
|
Package.onUse(function(api) {
|
||||||
|
api.versionsFrom('METEOR@0.9.0.1');
|
||||||
|
api.versionsFrom('1.5.2.2');
|
||||||
|
|
||||||
|
|
||||||
|
//TODO: Figure out how to reference the NPM modules instead of copying out of them into this local atmosphere package!
|
||||||
|
api.use('jquery');
|
||||||
|
//api.mainModule('jquery-custom-scrollbar.js');
|
||||||
|
api.addFiles('jquery.mousewheel.js', 'client');
|
||||||
|
api.addFiles('jquery.mCustomScrollbar.js', 'client');
|
||||||
|
api.addFiles('jquery.mCustomScrollbar.css', 'client');
|
||||||
|
});
|
||||||
|
|
||||||
|
//Npm.depends({
|
||||||
|
// "jquery-mousewheel": "^3.1.13",
|
||||||
|
// "malihu-custom-scrollbar-plugin": "^3.1.5"
|
||||||
|
//});
|
||||||
19
packages/jquery-custom-scrollbar/versions.json
Normal file
19
packages/jquery-custom-scrollbar/versions.json
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
{
|
||||||
|
"dependencies": [
|
||||||
|
[
|
||||||
|
"jquery",
|
||||||
|
"1.0.0"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"meteor",
|
||||||
|
"1.0.2"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"underscore",
|
||||||
|
"1.0.0"
|
||||||
|
]
|
||||||
|
],
|
||||||
|
"pluginDependencies": [],
|
||||||
|
"toolVersion": "meteor-tool@1.0.26",
|
||||||
|
"format": "1.0"
|
||||||
|
}
|
||||||
33
server/enhanceSaleDateFields.js
Normal file
33
server/enhanceSaleDateFields.js
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
|
||||||
|
//
|
||||||
|
// This routine will update the database sales records to take the sales.date field which is YYYYMMDD and add sales.timestamp and sales.weekOfYear fields that duplicate the sales.date field but provide enhanced search capabilities.
|
||||||
|
// This routine does not change sales.date. It can be run repeatedly without any problems.
|
||||||
|
//
|
||||||
|
// The problems this solves:
|
||||||
|
// 1. Searching by the week of the year is faster since it does not need to be calculated.
|
||||||
|
// 2. Searching by using date routines is easier since a conversion from string to date is not required (it is also apparently not possible in the current version of MongoDB).
|
||||||
|
// 3. Keeping the date field as YYYYMMDD makes sorting and avoiding timezone conversion errors easier.
|
||||||
|
//
|
||||||
|
|
||||||
|
Meteor.methods({
|
||||||
|
// Cleans up all Date objects that don't have a time component. Removes any time component.
|
||||||
|
"enhanceSalesDateFields": function() {
|
||||||
|
let sales = Sales.find({}).fetch();
|
||||||
|
|
||||||
|
for(let i = 0; i < sales.length; i++) {
|
||||||
|
let dateString = sales[i].date.toString();
|
||||||
|
dateString = dateString.substring(0, 4) + "-" + dateString.substring(4,6) + "-" + dateString.substring(6,8) + "T00:00:00Z";
|
||||||
|
let timestamp = new Date(dateString);
|
||||||
|
let weekOfYear = timestamp.getWeek();
|
||||||
|
|
||||||
|
//console.log("Converted " + sales[i].date + " to " + timestamp + " using " + dateString);
|
||||||
|
|
||||||
|
// Save to the database.
|
||||||
|
Sales.update(sales[i]._id, {$set: {timestamp, weekOfYear}}, {bypassCollection2: true}, function(err, id) {
|
||||||
|
if(err) console.log(err);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log("Finished enhancing the dates of the Sales entries.");
|
||||||
|
}
|
||||||
|
});
|
||||||
Reference in New Issue
Block a user