Added Roles, User Management, fixed bugs, added FlexTable component (should be renamed to GridTable), other table components and test code should be removed down the line, added admin function to fix broken data structures.
This commit is contained in:
5
.idea/codeStyles/codeStyleConfig.xml
generated
Normal file
5
.idea/codeStyles/codeStyleConfig.xml
generated
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
<component name="ProjectCodeStyleConfiguration">
|
||||||
|
<state>
|
||||||
|
<option name="PREFERRED_PROJECT_CODE_STYLE" value="Default" />
|
||||||
|
</state>
|
||||||
|
</component>
|
||||||
@@ -6,12 +6,12 @@
|
|||||||
|
|
||||||
meteor-base@1.5.1 # Packages every Meteor app needs to have
|
meteor-base@1.5.1 # Packages every Meteor app needs to have
|
||||||
mobile-experience@1.1.0 # Packages for a great mobile UX
|
mobile-experience@1.1.0 # Packages for a great mobile UX
|
||||||
mongo@1.14.6 # The database Meteor supports right now
|
mongo@1.15.0 # The database Meteor supports right now
|
||||||
jquery # Wrapper package for npm-installed jquery
|
jquery # Wrapper package for npm-installed jquery
|
||||||
reactive-var@1.0.11 # Reactive variable for tracker
|
reactive-var@1.0.11 # Reactive variable for tracker
|
||||||
tracker@1.2.0 # Meteor's client-side reactive programming library
|
tracker@1.2.0 # Meteor's client-side reactive programming library
|
||||||
|
|
||||||
standard-minifier-css@1.8.0 # CSS minifier run for production mode
|
standard-minifier-css@1.8.1 # CSS minifier run for production mode
|
||||||
standard-minifier-js@2.8.0 # JS minifier run for production mode
|
standard-minifier-js@2.8.0 # JS minifier run for production mode
|
||||||
es5-shim@4.8.0 # ECMAScript 5 compatibility for older browsers
|
es5-shim@4.8.0 # ECMAScript 5 compatibility for older browsers
|
||||||
ecmascript@0.16.2 # Enable ECMAScript2015+ syntax in app code
|
ecmascript@0.16.2 # Enable ECMAScript2015+ syntax in app code
|
||||||
@@ -22,7 +22,7 @@ svelte:compiler
|
|||||||
#static-html@1.3.2
|
#static-html@1.3.2
|
||||||
rdb:svelte-meteor-data
|
rdb:svelte-meteor-data
|
||||||
accounts-ui@1.4.2
|
accounts-ui@1.4.2
|
||||||
accounts-password@2.3.0
|
accounts-password@2.3.1
|
||||||
svelte:blaze-integration
|
svelte:blaze-integration
|
||||||
meteortesting:mocha
|
meteortesting:mocha
|
||||||
accounts-google@1.4.0
|
accounts-google@1.4.0
|
||||||
@@ -30,3 +30,6 @@ service-configuration@1.3.0
|
|||||||
google-config-ui@1.0.3 # Adds the UI for logging in via Google
|
google-config-ui@1.0.3 # Adds the UI for logging in via Google
|
||||||
alanning:roles # Adds roles to the user
|
alanning:roles # Adds roles to the user
|
||||||
#zodern:melte
|
#zodern:melte
|
||||||
|
|
||||||
|
#babrahams:constellation # Alternative to MeteorToys - Has problems because it requires jquery 1.11.11 and everything else wants 2.x or 3.x
|
||||||
|
msavin:mongol # Free version of MeteorToys - Provides access to the client side MongoDB for debugging. (Ctrl-M to activate :: https://atmospherejs.com/msavin/mongol)
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
METEOR@2.7
|
METEOR@2.7.2
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
accounts-base@2.2.2
|
accounts-base@2.2.3
|
||||||
accounts-google@1.4.0
|
accounts-google@1.4.0
|
||||||
accounts-oauth@1.4.1
|
accounts-oauth@1.4.1
|
||||||
accounts-password@2.3.1
|
accounts-password@2.3.1
|
||||||
@@ -11,8 +11,8 @@ babel-compiler@7.9.0
|
|||||||
babel-runtime@1.5.0
|
babel-runtime@1.5.0
|
||||||
base64@1.0.12
|
base64@1.0.12
|
||||||
binary-heap@1.0.11
|
binary-heap@1.0.11
|
||||||
blaze@2.5.0
|
blaze@2.6.0
|
||||||
blaze-tools@1.1.2
|
blaze-tools@1.1.3
|
||||||
boilerplate-generator@1.7.1
|
boilerplate-generator@1.7.1
|
||||||
caching-compiler@1.2.2
|
caching-compiler@1.2.2
|
||||||
caching-html-compiler@1.2.1
|
caching-html-compiler@1.2.1
|
||||||
@@ -37,14 +37,14 @@ geojson-utils@1.0.10
|
|||||||
google-config-ui@1.0.3
|
google-config-ui@1.0.3
|
||||||
google-oauth@1.4.2
|
google-oauth@1.4.2
|
||||||
hot-code-push@1.0.4
|
hot-code-push@1.0.4
|
||||||
html-tools@1.1.2
|
html-tools@1.1.3
|
||||||
htmljs@1.1.1
|
htmljs@1.1.1
|
||||||
http@1.4.4
|
http@2.0.0
|
||||||
id-map@1.1.1
|
id-map@1.1.1
|
||||||
inter-process-messaging@0.1.1
|
inter-process-messaging@0.1.1
|
||||||
jquery@3.0.0
|
jquery@3.0.0
|
||||||
launch-screen@1.3.0
|
launch-screen@1.3.0
|
||||||
less@3.0.2
|
less@4.0.0
|
||||||
localstorage@1.2.0
|
localstorage@1.2.0
|
||||||
logging@1.3.1
|
logging@1.3.1
|
||||||
meteor@1.10.0
|
meteor@1.10.0
|
||||||
@@ -52,27 +52,29 @@ meteor-base@1.5.1
|
|||||||
meteortesting:browser-tests@1.3.5
|
meteortesting:browser-tests@1.3.5
|
||||||
meteortesting:mocha@2.0.3
|
meteortesting:mocha@2.0.3
|
||||||
meteortesting:mocha-core@8.1.2
|
meteortesting:mocha-core@8.1.2
|
||||||
|
meteortoys:toykit@10.0.0
|
||||||
minifier-css@1.6.0
|
minifier-css@1.6.0
|
||||||
minifier-js@2.7.4
|
minifier-js@2.7.4
|
||||||
minimongo@1.8.0
|
minimongo@1.8.0
|
||||||
mobile-experience@1.1.0
|
mobile-experience@1.1.0
|
||||||
mobile-status-bar@1.1.0
|
mobile-status-bar@1.1.0
|
||||||
modern-browsers@0.1.7
|
modern-browsers@0.1.8
|
||||||
modules@0.18.0
|
modules@0.18.0
|
||||||
modules-runtime@0.13.0
|
modules-runtime@0.13.0
|
||||||
mongo@1.14.6
|
mongo@1.15.0
|
||||||
mongo-decimal@0.1.2
|
mongo-decimal@0.1.3
|
||||||
mongo-dev-server@1.1.0
|
mongo-dev-server@1.1.0
|
||||||
mongo-id@1.0.8
|
mongo-id@1.0.8
|
||||||
|
msavin:mongol@10.0.1
|
||||||
npm-mongo@4.3.1
|
npm-mongo@4.3.1
|
||||||
oauth@2.1.2
|
oauth@2.1.2
|
||||||
oauth2@1.3.1
|
oauth2@1.3.1
|
||||||
observe-sequence@1.0.19
|
observe-sequence@1.0.20
|
||||||
ordered-dict@1.1.0
|
ordered-dict@1.1.0
|
||||||
promise@0.12.0
|
promise@0.12.0
|
||||||
random@1.2.0
|
random@1.2.0
|
||||||
rate-limit@1.0.9
|
rate-limit@1.0.9
|
||||||
rdb:svelte-meteor-data@0.3.1
|
rdb:svelte-meteor-data@1.0.0
|
||||||
react-fast-refresh@0.2.3
|
react-fast-refresh@0.2.3
|
||||||
reactive-dict@1.3.0
|
reactive-dict@1.3.0
|
||||||
reactive-var@1.0.11
|
reactive-var@1.0.11
|
||||||
@@ -83,17 +85,18 @@ service-configuration@1.3.0
|
|||||||
session@1.2.0
|
session@1.2.0
|
||||||
sha@1.0.9
|
sha@1.0.9
|
||||||
shell-server@0.5.0
|
shell-server@0.5.0
|
||||||
socket-stream-client@0.4.0
|
socket-stream-client@0.5.0
|
||||||
spacebars@1.2.0
|
spacebars@1.3.0
|
||||||
spacebars-compiler@1.3.0
|
spacebars-compiler@1.3.1
|
||||||
standard-minifier-css@1.8.1
|
standard-minifier-css@1.8.1
|
||||||
standard-minifier-js@2.8.0
|
standard-minifier-js@2.8.0
|
||||||
svelte:blaze-integration@0.4.0
|
svelte:blaze-integration@0.4.0
|
||||||
svelte:compiler@3.46.4
|
svelte:compiler@3.46.4
|
||||||
templating@1.4.1
|
templating@1.4.2
|
||||||
templating-compiler@1.4.1
|
templating-compiler@1.4.1
|
||||||
templating-runtime@1.5.0
|
templating-runtime@1.6.0
|
||||||
templating-tools@1.2.1
|
templating-tools@1.2.2
|
||||||
|
tmeasday:check-npm-versions@1.0.2
|
||||||
tracker@1.2.0
|
tracker@1.2.0
|
||||||
typescript@4.5.4
|
typescript@4.5.4
|
||||||
underscore@1.0.10
|
underscore@1.0.10
|
||||||
|
|||||||
3
client/app.css
Normal file
3
client/app.css
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
|
||||||
|
|
||||||
|
/*# sourceMappingURL=app.css.map */
|
||||||
1
client/app.css.map
Normal file
1
client/app.css.map
Normal file
@@ -0,0 +1 @@
|
|||||||
|
{"version":3,"sourceRoot":"","sources":[],"names":[],"mappings":"","file":"app.css"}
|
||||||
108
client/app.sass
Normal file
108
client/app.sass
Normal file
@@ -0,0 +1,108 @@
|
|||||||
|
/* CSS declarations go here */
|
||||||
|
.Mongol_row, .Mongol_row_name
|
||||||
|
color: white
|
||||||
|
|
||||||
|
*
|
||||||
|
box-sizing: border-box
|
||||||
|
|
||||||
|
html
|
||||||
|
padding: 0
|
||||||
|
margin: 0
|
||||||
|
|
||||||
|
body
|
||||||
|
font-family: sans-serif
|
||||||
|
background-attachment: fixed
|
||||||
|
position: absolute
|
||||||
|
top: 0
|
||||||
|
bottom: 0
|
||||||
|
left: 0
|
||||||
|
right: 0
|
||||||
|
padding: 0
|
||||||
|
margin: 0
|
||||||
|
|
||||||
|
//.container
|
||||||
|
// max-width: 600px
|
||||||
|
// margin: 0 auto
|
||||||
|
// min-height: 100%
|
||||||
|
// background: white
|
||||||
|
|
||||||
|
h1
|
||||||
|
font-size: 1.5em
|
||||||
|
margin: 0 0 1rem 0
|
||||||
|
display: inline-block
|
||||||
|
|
||||||
|
form
|
||||||
|
margin-top: 10px
|
||||||
|
margin-bottom: -10px
|
||||||
|
position: relative
|
||||||
|
|
||||||
|
.new-task input
|
||||||
|
box-sizing: border-box
|
||||||
|
padding: 10px 0
|
||||||
|
background: transparent
|
||||||
|
border: none
|
||||||
|
width: 100%
|
||||||
|
padding-right: 80px
|
||||||
|
font-size: 1em
|
||||||
|
|
||||||
|
.new-task input:focus
|
||||||
|
outline: 0
|
||||||
|
|
||||||
|
ul
|
||||||
|
margin: 0
|
||||||
|
padding: 0
|
||||||
|
background: white
|
||||||
|
|
||||||
|
.delete
|
||||||
|
float: right
|
||||||
|
font-weight: bold
|
||||||
|
background: none
|
||||||
|
font-size: 1em
|
||||||
|
border: none
|
||||||
|
position: relative
|
||||||
|
|
||||||
|
li
|
||||||
|
position: relative
|
||||||
|
list-style: none
|
||||||
|
padding: 15px
|
||||||
|
border-bottom: #eee solid 1px
|
||||||
|
|
||||||
|
li .text
|
||||||
|
margin-left: 10px
|
||||||
|
|
||||||
|
li.checked
|
||||||
|
color: #888
|
||||||
|
|
||||||
|
li.checked .text
|
||||||
|
text-decoration: line-through
|
||||||
|
|
||||||
|
li.private
|
||||||
|
background: #eee
|
||||||
|
border-color: #ddd
|
||||||
|
|
||||||
|
header .hide-completed
|
||||||
|
float: right
|
||||||
|
|
||||||
|
.toggle-private
|
||||||
|
margin-left: 5px
|
||||||
|
|
||||||
|
html
|
||||||
|
font-size: 16px
|
||||||
|
|
||||||
|
@media (max-width: 900px)
|
||||||
|
html
|
||||||
|
font-size: 14px
|
||||||
|
|
||||||
|
@media (max-width: 600px)
|
||||||
|
li
|
||||||
|
padding: 12px 15px
|
||||||
|
|
||||||
|
.search
|
||||||
|
width: 150px
|
||||||
|
clear: both
|
||||||
|
|
||||||
|
.new-task input
|
||||||
|
padding-bottom: 5px
|
||||||
|
|
||||||
|
html
|
||||||
|
font-size: 10px
|
||||||
244
client/main.css
244
client/main.css
@@ -1,117 +1,4 @@
|
|||||||
/* CSS declarations go here */
|
|
||||||
@import url(https://fonts.googleapis.com/css?family=Lato:400,300,300italic,400italic,700,700italic);
|
@import url(https://fonts.googleapis.com/css?family=Lato:400,300,300italic,400italic,700,700italic);
|
||||||
body {
|
|
||||||
padding: 10px;
|
|
||||||
font-family: sans-serif;
|
|
||||||
background-attachment: fixed;
|
|
||||||
position: absolute;
|
|
||||||
top: 0;
|
|
||||||
bottom: 0;
|
|
||||||
left: 0;
|
|
||||||
right: 0;
|
|
||||||
padding: 0;
|
|
||||||
margin: 0;
|
|
||||||
font-size: 14px;
|
|
||||||
}
|
|
||||||
|
|
||||||
header {
|
|
||||||
background: #d2edf4;
|
|
||||||
background-image: linear-gradient(to bottom, #d0edf5, #e1e5f0 100%);
|
|
||||||
padding: 20px 15px 15px 15px;
|
|
||||||
position: relative;
|
|
||||||
}
|
|
||||||
|
|
||||||
#login-buttons {
|
|
||||||
display: block;
|
|
||||||
}
|
|
||||||
|
|
||||||
h1 {
|
|
||||||
font-size: 1.5em;
|
|
||||||
margin: 0 0 1rem 0;
|
|
||||||
display: inline-block;
|
|
||||||
}
|
|
||||||
|
|
||||||
form {
|
|
||||||
margin-top: 10px;
|
|
||||||
margin-bottom: -10px;
|
|
||||||
position: relative;
|
|
||||||
}
|
|
||||||
|
|
||||||
.new-task input {
|
|
||||||
box-sizing: border-box;
|
|
||||||
padding: 10px 0;
|
|
||||||
background: transparent;
|
|
||||||
border: none;
|
|
||||||
width: 100%;
|
|
||||||
padding-right: 80px;
|
|
||||||
font-size: 1em;
|
|
||||||
}
|
|
||||||
|
|
||||||
.new-task input:focus {
|
|
||||||
outline: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
ul {
|
|
||||||
margin: 0;
|
|
||||||
padding: 0;
|
|
||||||
background: white;
|
|
||||||
}
|
|
||||||
|
|
||||||
.delete {
|
|
||||||
float: right;
|
|
||||||
font-weight: bold;
|
|
||||||
background: none;
|
|
||||||
font-size: 1em;
|
|
||||||
border: none;
|
|
||||||
position: relative;
|
|
||||||
}
|
|
||||||
|
|
||||||
li {
|
|
||||||
position: relative;
|
|
||||||
list-style: none;
|
|
||||||
padding: 15px;
|
|
||||||
border-bottom: #eee solid 1px;
|
|
||||||
}
|
|
||||||
|
|
||||||
li .text {
|
|
||||||
margin-left: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
li.checked {
|
|
||||||
color: #888;
|
|
||||||
}
|
|
||||||
|
|
||||||
li.checked .text {
|
|
||||||
text-decoration: line-through;
|
|
||||||
}
|
|
||||||
|
|
||||||
li.private {
|
|
||||||
background: #eee;
|
|
||||||
border-color: #ddd;
|
|
||||||
}
|
|
||||||
|
|
||||||
header .hide-completed {
|
|
||||||
float: right;
|
|
||||||
}
|
|
||||||
|
|
||||||
.toggle-private {
|
|
||||||
margin-left: 5px;
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (max-width: 600px) {
|
|
||||||
li {
|
|
||||||
padding: 12px 15px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.search {
|
|
||||||
width: 150px;
|
|
||||||
clear: both;
|
|
||||||
}
|
|
||||||
|
|
||||||
.new-task input {
|
|
||||||
padding-bottom: 5px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
html, body {
|
html, body {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
@@ -119,7 +6,6 @@ html, body {
|
|||||||
padding: 0;
|
padding: 0;
|
||||||
left: 0;
|
left: 0;
|
||||||
top: 0;
|
top: 0;
|
||||||
font-size: 100%;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ROOT FONT STYLES */
|
/* ROOT FONT STYLES */
|
||||||
@@ -193,14 +79,13 @@ p {
|
|||||||
|
|
||||||
/* ==== GRID SYSTEM ==== */
|
/* ==== GRID SYSTEM ==== */
|
||||||
.container {
|
.container {
|
||||||
width: 90%;
|
width: 100%;
|
||||||
margin-left: auto;
|
margin-left: auto;
|
||||||
margin-right: auto;
|
margin-right: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
.row {
|
.row {
|
||||||
position: relative;
|
position: relative;
|
||||||
width: 100%;
|
|
||||||
}
|
}
|
||||||
.row [class^=col] {
|
.row [class^=col] {
|
||||||
float: left;
|
float: left;
|
||||||
@@ -210,6 +95,7 @@ p {
|
|||||||
|
|
||||||
.col-1, .col-2, .col-3, .col-4, .col-5, .col-6, .col-7, .col-8, .col-9, .col-10, .col-11, .col-12 {
|
.col-1, .col-2, .col-3, .col-4, .col-5, .col-6, .col-7, .col-8, .col-9, .col-10, .col-11, .col-12 {
|
||||||
width: 96%;
|
width: 96%;
|
||||||
|
margin: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
.col-1-sm {
|
.col-1-sm {
|
||||||
@@ -337,5 +223,131 @@ p {
|
|||||||
max-width: 60rem;
|
max-width: 60rem;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
/* CSS declarations go here */
|
||||||
|
.Mongol_row, .Mongol_row_name {
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
* {
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
html {
|
||||||
|
padding: 0;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
font-family: sans-serif;
|
||||||
|
background-attachment: fixed;
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
bottom: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
padding: 0;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
h1 {
|
||||||
|
font-size: 1.5em;
|
||||||
|
margin: 0 0 1rem 0;
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
|
||||||
|
form {
|
||||||
|
margin-top: 10px;
|
||||||
|
margin-bottom: -10px;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.new-task input {
|
||||||
|
box-sizing: border-box;
|
||||||
|
padding: 10px 0;
|
||||||
|
background: transparent;
|
||||||
|
border: none;
|
||||||
|
width: 100%;
|
||||||
|
padding-right: 80px;
|
||||||
|
font-size: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.new-task input:focus {
|
||||||
|
outline: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
ul {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
background: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.delete {
|
||||||
|
float: right;
|
||||||
|
font-weight: bold;
|
||||||
|
background: none;
|
||||||
|
font-size: 1em;
|
||||||
|
border: none;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
li {
|
||||||
|
position: relative;
|
||||||
|
list-style: none;
|
||||||
|
padding: 15px;
|
||||||
|
border-bottom: #eee solid 1px;
|
||||||
|
}
|
||||||
|
|
||||||
|
li .text {
|
||||||
|
margin-left: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
li.checked {
|
||||||
|
color: #888;
|
||||||
|
}
|
||||||
|
|
||||||
|
li.checked .text {
|
||||||
|
text-decoration: line-through;
|
||||||
|
}
|
||||||
|
|
||||||
|
li.private {
|
||||||
|
background: #eee;
|
||||||
|
border-color: #ddd;
|
||||||
|
}
|
||||||
|
|
||||||
|
header .hide-completed {
|
||||||
|
float: right;
|
||||||
|
}
|
||||||
|
|
||||||
|
.toggle-private {
|
||||||
|
margin-left: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
html {
|
||||||
|
font-size: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 900px) {
|
||||||
|
html {
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@media (max-width: 600px) {
|
||||||
|
li {
|
||||||
|
padding: 12px 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.search {
|
||||||
|
width: 150px;
|
||||||
|
clear: both;
|
||||||
|
}
|
||||||
|
|
||||||
|
.new-task input {
|
||||||
|
padding-bottom: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
html {
|
||||||
|
font-size: 10px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/*# sourceMappingURL=main.css.map */
|
/*# sourceMappingURL=main.css.map */
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
{"version":3,"sourceRoot":"","sources":["main.sass","simple-grid.sass"],"names":[],"mappings":"AAAA;ACAQ;ADCR;EACC;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;;AAQD;EACC;EACA;EACA;EACA;;;AAED;EACC;;;AAED;EACC;EACA;EACA;;;AAED;EACC;EACA;EACA;;;AAED;EACC;EACA;EACA;EACA;EACA;EACA;EACA;;;AAED;EACC;;;AAED;EACC;EACA;EACA;;;AAED;EACC;EACA;EACA;EACA;EACA;EACA;;;AAED;EACC;EACA;EACA;EACA;;;AAED;EACC;;;AAED;EACC;;;AAED;EACC;;;AAED;EACC;EACA;;;AAED;EACC;;;AAED;EACC;;;AAED;EACC;IACC;;;EAED;IACC;IACA;;;EAED;IACC;;;AChGF;EACC;EACA;EACA;EACA;EACA;EACA;EACA;;;AAED;AAEA;EACC;EACA;EACA;;;AAED;AAEA;EACC;;;AAED;EACC;;;AAED;EACC;;;AAED;EACC;;;AAED;EACC;;;AAED;EACC;;;AAED;EACC;EACA;EACA;;;AAED;EACC;;;AAED;EACC;;;AAED;EACC;;;AAED;AAEA;EACC;;;AAED;EACC;;;AAED;EACC;EACA;EACA;;;AAED;EACC;;;AAED;AAEA;EACC;EACA;EACA;;;AAED;EACC;EACA;;AAEA;EACC;EACA;EACA;;;AAEF;EACC;;;AAED;EACC;;;AAED;EACC;;;AAED;EACC;;;AAED;EACC;;;AAED;EACC;;;AAED;EACC;;;AAED;EACC;;;AAED;EACC;;;AAED;EACC;;;AAED;EACC;;;AAED;EACC;;;AAED;EACC;;;AAED;EACC;EACA;EACA;;;AAED;EACC;;;AAED;AACC;EAEA;IACC;;;AAEF;AACC;EAEA;IACC;;;EAED;IACC;;;EAED;IACC;;;EAED;IACC;;;EAED;IACC;;;EAED;IACC;;;EAED;IACC;;;EAED;IACC;;;EAED;IACC;;;EAED;IACC;;;EAED;IACC;;;EAED;IACC;;;EAED;IACC;;;AAEF;AACC;EAEA;IACC;IACA","file":"main.css"}
|
{"version":3,"sourceRoot":"","sources":["simple-grid.sass","app.sass"],"names":[],"mappings":"AAAQ;AAER;EACC;EACA;EACA;EACA;EACA;EACA;;;AAED;AAEA;EACC;EACA;EACA;;;AAED;AAEA;EACC;;;AAED;EACC;;;AAED;EACC;;;AAED;EACC;;;AAED;EACC;;;AAED;EACC;;;AAED;EACC;EACA;EACA;;;AAED;EACC;;;AAED;EACC;;;AAED;EACC;;;AAED;AAEA;EACC;;;AAED;EACC;;;AAED;EACC;EACA;EACA;;;AAED;EACC;;;AAED;AAEA;EAEC;EACA;EACA;;;AAED;EACC;;AAGA;EACC;EACA;EACA;;;AAEF;EACC;EACA;;;AAED;EACC;;;AAED;EACC;;;AAED;EACC;;;AAED;EACC;;;AAED;EACC;;;AAED;EACC;;;AAED;EACC;;;AAED;EACC;;;AAED;EACC;;;AAED;EACC;;;AAED;EACC;;;AAED;EACC;;;AAED;EACC;EACA;EACA;;;AAED;EACC;;;AAED;AACC;EAEA;IACC;;;AAEF;AACC;EAEA;IACC;;;EAED;IACC;;;EAED;IACC;;;EAED;IACC;;;EAED;IACC;;;EAED;IACC;;;EAED;IACC;;;EAED;IACC;;;EAED;IACC;;;EAED;IACC;;;EAED;IACC;;;EAED;IACC;;;EAED;IACC;;;AAEF;AACC;EAEA;IACC;IACA;;;ACzLF;AACA;EACC;;;AAED;EACC;;;AAED;EACC;EACA;;;AAED;EACC;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;;AAQD;EACC;EACA;EACA;;;AAED;EACC;EACA;EACA;;;AAED;EACC;EACA;EACA;EACA;EACA;EACA;EACA;;;AAED;EACC;;;AAED;EACC;EACA;EACA;;;AAED;EACC;EACA;EACA;EACA;EACA;EACA;;;AAED;EACC;EACA;EACA;EACA;;;AAED;EACC;;;AAED;EACC;;;AAED;EACC;;;AAED;EACC;EACA;;;AAED;EACC;;;AAED;EACC;;;AAED;EACC;;;AAED;EACC;IACC;;;AAEF;EACC;IACC;;;EAED;IACC;IACA;;;EAED;IACC;;;EAED;IACC","file":"main.css"}
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
<head>
|
<head>
|
||||||
<title>District Central</title>
|
<title>District Central</title>
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
|
|||||||
@@ -1,101 +1,4 @@
|
|||||||
/* CSS declarations go here */
|
|
||||||
body
|
|
||||||
padding: 10px
|
|
||||||
font-family: sans-serif
|
|
||||||
background-attachment: fixed
|
|
||||||
position: absolute
|
|
||||||
top: 0
|
|
||||||
bottom: 0
|
|
||||||
left: 0
|
|
||||||
right: 0
|
|
||||||
padding: 0
|
|
||||||
margin: 0
|
|
||||||
font-size: 14px
|
|
||||||
|
|
||||||
//.container
|
|
||||||
// max-width: 600px
|
|
||||||
// margin: 0 auto
|
|
||||||
// min-height: 100%
|
|
||||||
// background: white
|
|
||||||
|
|
||||||
header
|
|
||||||
background: #d2edf4
|
|
||||||
background-image: linear-gradient(to bottom, #d0edf5, #e1e5f0 100%)
|
|
||||||
padding: 20px 15px 15px 15px
|
|
||||||
position: relative
|
|
||||||
|
|
||||||
#login-buttons
|
|
||||||
display: block
|
|
||||||
|
|
||||||
h1
|
|
||||||
font-size: 1.5em
|
|
||||||
margin: 0 0 1rem 0
|
|
||||||
display: inline-block
|
|
||||||
|
|
||||||
form
|
|
||||||
margin-top: 10px
|
|
||||||
margin-bottom: -10px
|
|
||||||
position: relative
|
|
||||||
|
|
||||||
.new-task input
|
|
||||||
box-sizing: border-box
|
|
||||||
padding: 10px 0
|
|
||||||
background: transparent
|
|
||||||
border: none
|
|
||||||
width: 100%
|
|
||||||
padding-right: 80px
|
|
||||||
font-size: 1em
|
|
||||||
|
|
||||||
.new-task input:focus
|
|
||||||
outline: 0
|
|
||||||
|
|
||||||
ul
|
|
||||||
margin: 0
|
|
||||||
padding: 0
|
|
||||||
background: white
|
|
||||||
|
|
||||||
.delete
|
|
||||||
float: right
|
|
||||||
font-weight: bold
|
|
||||||
background: none
|
|
||||||
font-size: 1em
|
|
||||||
border: none
|
|
||||||
position: relative
|
|
||||||
|
|
||||||
li
|
|
||||||
position: relative
|
|
||||||
list-style: none
|
|
||||||
padding: 15px
|
|
||||||
border-bottom: #eee solid 1px
|
|
||||||
|
|
||||||
li .text
|
|
||||||
margin-left: 10px
|
|
||||||
|
|
||||||
li.checked
|
|
||||||
color: #888
|
|
||||||
|
|
||||||
li.checked .text
|
|
||||||
text-decoration: line-through
|
|
||||||
|
|
||||||
li.private
|
|
||||||
background: #eee
|
|
||||||
border-color: #ddd
|
|
||||||
|
|
||||||
header .hide-completed
|
|
||||||
float: right
|
|
||||||
|
|
||||||
.toggle-private
|
|
||||||
margin-left: 5px
|
|
||||||
|
|
||||||
@media (max-width: 600px)
|
|
||||||
li
|
|
||||||
padding: 12px 15px
|
|
||||||
|
|
||||||
.search
|
|
||||||
width: 150px
|
|
||||||
clear: both
|
|
||||||
|
|
||||||
.new-task input
|
|
||||||
padding-bottom: 5px
|
|
||||||
|
|
||||||
@import "./simple-grid.sass"
|
@import "./simple-grid.sass"
|
||||||
|
@import "./app.sass"
|
||||||
|
|||||||
@@ -1,228 +0,0 @@
|
|||||||
@import url(https://fonts.googleapis.com/css?family=Lato:400,300,300italic,400italic,700,700italic);
|
|
||||||
html, body {
|
|
||||||
height: 100%;
|
|
||||||
width: 100%;
|
|
||||||
margin: 0;
|
|
||||||
padding: 0;
|
|
||||||
left: 0;
|
|
||||||
top: 0;
|
|
||||||
font-size: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ROOT FONT STYLES */
|
|
||||||
* {
|
|
||||||
font-family: "Lato", Helvetica, sans-serif;
|
|
||||||
color: #333447;
|
|
||||||
line-height: 1.5;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* TYPOGRAPHY */
|
|
||||||
h1 {
|
|
||||||
font-size: 2.5rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
h2 {
|
|
||||||
font-size: 2rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
h3 {
|
|
||||||
font-size: 1.375rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
h4 {
|
|
||||||
font-size: 1.125rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
h5 {
|
|
||||||
font-size: 1rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
h6 {
|
|
||||||
font-size: 0.875rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
p {
|
|
||||||
font-size: 1.125rem;
|
|
||||||
font-weight: 200;
|
|
||||||
line-height: 1.8;
|
|
||||||
}
|
|
||||||
|
|
||||||
.font-light {
|
|
||||||
font-weight: 300;
|
|
||||||
}
|
|
||||||
|
|
||||||
.font-regular {
|
|
||||||
font-weight: 400;
|
|
||||||
}
|
|
||||||
|
|
||||||
.font-heavy {
|
|
||||||
font-weight: 700;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* POSITIONING */
|
|
||||||
.left {
|
|
||||||
text-align: left;
|
|
||||||
}
|
|
||||||
|
|
||||||
.right {
|
|
||||||
text-align: right;
|
|
||||||
}
|
|
||||||
|
|
||||||
.center {
|
|
||||||
text-align: center;
|
|
||||||
margin-left: auto;
|
|
||||||
margin-right: auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
.justify {
|
|
||||||
text-align: justify;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ==== GRID SYSTEM ==== */
|
|
||||||
.container {
|
|
||||||
width: 90%;
|
|
||||||
margin-left: auto;
|
|
||||||
margin-right: auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
.row {
|
|
||||||
position: relative;
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
.row [class^=col] {
|
|
||||||
float: left;
|
|
||||||
margin: 0.5rem 2%;
|
|
||||||
min-height: 0.125rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.col-1, .col-2, .col-3, .col-4, .col-5, .col-6, .col-7, .col-8, .col-9, .col-10, .col-11, .col-12 {
|
|
||||||
width: 96%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.col-1-sm {
|
|
||||||
width: 4.33%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.col-2-sm {
|
|
||||||
width: 12.66%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.col-3-sm {
|
|
||||||
width: 21%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.col-4-sm {
|
|
||||||
width: 29.33%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.col-5-sm {
|
|
||||||
width: 37.66%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.col-6-sm {
|
|
||||||
width: 46%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.col-7-sm {
|
|
||||||
width: 54.33%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.col-8-sm {
|
|
||||||
width: 62.66%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.col-9-sm {
|
|
||||||
width: 71%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.col-10-sm {
|
|
||||||
width: 79.33%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.col-11-sm {
|
|
||||||
width: 87.66%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.col-12-sm {
|
|
||||||
width: 96%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.row::after {
|
|
||||||
content: "";
|
|
||||||
display: table;
|
|
||||||
clear: both;
|
|
||||||
}
|
|
||||||
|
|
||||||
.hidden-sm {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
@media only screen and (min-width: 33.75em) {
|
|
||||||
/* 540px */
|
|
||||||
.container {
|
|
||||||
width: 80%;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@media only screen and (min-width: 45em) {
|
|
||||||
/* 720px */
|
|
||||||
.col-1 {
|
|
||||||
width: 4.33%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.col-2 {
|
|
||||||
width: 12.66%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.col-3 {
|
|
||||||
width: 21%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.col-4 {
|
|
||||||
width: 29.33%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.col-5 {
|
|
||||||
width: 37.66%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.col-6 {
|
|
||||||
width: 46%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.col-7 {
|
|
||||||
width: 54.33%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.col-8 {
|
|
||||||
width: 62.66%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.col-9 {
|
|
||||||
width: 71%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.col-10 {
|
|
||||||
width: 79.33%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.col-11 {
|
|
||||||
width: 87.66%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.col-12 {
|
|
||||||
width: 96%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.hidden-sm {
|
|
||||||
display: block;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@media only screen and (min-width: 60em) {
|
|
||||||
/* 960px */
|
|
||||||
.container {
|
|
||||||
width: 75%;
|
|
||||||
max-width: 60rem;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*# sourceMappingURL=simple-grid.css.map */
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
{"version":3,"sourceRoot":"","sources":["simple-grid.sass"],"names":[],"mappings":"AAAQ;AAER;EACC;EACA;EACA;EACA;EACA;EACA;EACA;;;AAED;AAEA;EACC;EACA;EACA;;;AAED;AAEA;EACC;;;AAED;EACC;;;AAED;EACC;;;AAED;EACC;;;AAED;EACC;;;AAED;EACC;;;AAED;EACC;EACA;EACA;;;AAED;EACC;;;AAED;EACC;;;AAED;EACC;;;AAED;AAEA;EACC;;;AAED;EACC;;;AAED;EACC;EACA;EACA;;;AAED;EACC;;;AAED;AAEA;EACC;EACA;EACA;;;AAED;EACC;EACA;;AAEA;EACC;EACA;EACA;;;AAEF;EACC;;;AAED;EACC;;;AAED;EACC;;;AAED;EACC;;;AAED;EACC;;;AAED;EACC;;;AAED;EACC;;;AAED;EACC;;;AAED;EACC;;;AAED;EACC;;;AAED;EACC;;;AAED;EACC;;;AAED;EACC;;;AAED;EACC;EACA;EACA;;;AAED;EACC;;;AAED;AACC;EAEA;IACC;;;AAEF;AACC;EAEA;IACC;;;EAED;IACC;;;EAED;IACC;;;EAED;IACC;;;EAED;IACC;;;EAED;IACC;;;EAED;IACC;;;EAED;IACC;;;EAED;IACC;;;EAED;IACC;;;EAED;IACC;;;EAED;IACC;;;EAED;IACC;;;AAEF;AACC;EAEA;IACC;IACA","file":"simple-grid.css"}
|
|
||||||
@@ -7,7 +7,6 @@ html, body
|
|||||||
padding: 0
|
padding: 0
|
||||||
left: 0
|
left: 0
|
||||||
top: 0
|
top: 0
|
||||||
font-size: 100%
|
|
||||||
|
|
||||||
/* ROOT FONT STYLES
|
/* ROOT FONT STYLES
|
||||||
|
|
||||||
@@ -69,13 +68,14 @@ p
|
|||||||
/* ==== GRID SYSTEM ====
|
/* ==== GRID SYSTEM ====
|
||||||
|
|
||||||
.container
|
.container
|
||||||
width: 90%
|
//width: 90%
|
||||||
|
width: 100%
|
||||||
margin-left: auto
|
margin-left: auto
|
||||||
margin-right: auto
|
margin-right: auto
|
||||||
|
|
||||||
.row
|
.row
|
||||||
position: relative
|
position: relative
|
||||||
width: 100%
|
//width: 100%
|
||||||
|
|
||||||
[class^="col"]
|
[class^="col"]
|
||||||
float: left
|
float: left
|
||||||
@@ -84,6 +84,7 @@ p
|
|||||||
|
|
||||||
.col-1, .col-2, .col-3, .col-4, .col-5, .col-6, .col-7, .col-8, .col-9, .col-10, .col-11, .col-12
|
.col-1, .col-2, .col-3, .col-4, .col-5, .col-6, .col-7, .col-8, .col-9, .col-10, .col-11, .col-12
|
||||||
width: 96%
|
width: 96%
|
||||||
|
margin: auto
|
||||||
|
|
||||||
.col-1-sm
|
.col-1-sm
|
||||||
width: 4.33%
|
width: 4.33%
|
||||||
|
|||||||
56
imports/api/admin.js
Normal file
56
imports/api/admin.js
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
import {Meteor} from "meteor/meteor";
|
||||||
|
import { _ } from 'underscore';
|
||||||
|
import { Roles } from 'meteor/alanning:roles';
|
||||||
|
|
||||||
|
if (Meteor.isServer) {
|
||||||
|
Meteor.methods({
|
||||||
|
'admin.fixRecords'(input) {
|
||||||
|
if(Roles.userIsInRole(Meteor.userId(), "admin", {anyScope:true})) {
|
||||||
|
console.log("In Fix Records");
|
||||||
|
console.log("Deleting invalid records...");
|
||||||
|
|
||||||
|
// Delete any records missing key fields.
|
||||||
|
Meteor.Records.remove({serial: {$exists: false}});
|
||||||
|
Meteor.Records.remove({deviceId: {$exists: false}});
|
||||||
|
Meteor.Records.remove({endTime: {$exists: false}});
|
||||||
|
|
||||||
|
console.log("Consolidating records...");
|
||||||
|
|
||||||
|
let emails = _.uniq(Meteor.Records.find({}, {
|
||||||
|
sort: {email: 1},
|
||||||
|
fields: {email: true}
|
||||||
|
}).fetch().map(function (x) {
|
||||||
|
return x.email;
|
||||||
|
}), true);
|
||||||
|
|
||||||
|
emails.forEach(email => {
|
||||||
|
// Find all records for the user sorted from oldest to newest.
|
||||||
|
let records = Meteor.Records.find({email}, {sort: {startTime: 1}}).fetch();
|
||||||
|
let newRecords = [];
|
||||||
|
let record = records[0];
|
||||||
|
|
||||||
|
for (let index = 1; index < records.length; index++) {
|
||||||
|
let nextRecord = records[index];
|
||||||
|
|
||||||
|
if (record.deviceId === nextRecord.deviceId) {
|
||||||
|
record.endTime = nextRecord.endTime;
|
||||||
|
record.count += nextRecord.count;
|
||||||
|
record.internalCount += nextRecord.internalCount;
|
||||||
|
} else {
|
||||||
|
if (!record.endTime) record.endTime = nextRecord.startTime;
|
||||||
|
newRecords.push(record);
|
||||||
|
record = nextRecord;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
newRecords.push(record);
|
||||||
|
|
||||||
|
Meteor.Records.remove({email});
|
||||||
|
|
||||||
|
for (let index = 0; index < newRecords.length; index++) {
|
||||||
|
Meteor.Records.insert(newRecords[index]);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
@@ -2,6 +2,7 @@ import { Meteor } from 'meteor/meteor';
|
|||||||
import { Mongo } from 'meteor/mongo';
|
import { Mongo } from 'meteor/mongo';
|
||||||
import { check } from 'meteor/check';
|
import { check } from 'meteor/check';
|
||||||
import { MongoClient } from 'mongodb';
|
import { MongoClient } from 'mongodb';
|
||||||
|
//import {Roles} from 'alanning/roles';
|
||||||
|
|
||||||
//export const Records = new Mongo.Collection('records');
|
//export const Records = new Mongo.Collection('records');
|
||||||
let client;
|
let client;
|
||||||
@@ -9,44 +10,83 @@ let database;
|
|||||||
let dataCollection;
|
let dataCollection;
|
||||||
|
|
||||||
if (Meteor.isServer) {
|
if (Meteor.isServer) {
|
||||||
let uri = process.env.MONGO_URL2;
|
// let uri = process.env.MONGO_URL2;
|
||||||
|
// uri = "mongodb://localhost:27017";
|
||||||
|
//
|
||||||
|
// //client = new MongoClient(uri);
|
||||||
|
// MongoClient.connect(uri, (err, c) => {
|
||||||
|
// client = c;
|
||||||
|
// database = client.db("avusd-data-collection");
|
||||||
|
// dataCollection = database.collection("records");
|
||||||
|
// dataCollection.find({deviceId: "1e3e99ef-adf4-4aa2-8784-205bc60f0ce3"}).toArray((e, a) => {
|
||||||
|
// if(e) console.log(e);
|
||||||
|
// else console.log("Found " + a.length + " records.");
|
||||||
|
// })
|
||||||
|
// });
|
||||||
|
|
||||||
client = new MongoClient(uri);
|
|
||||||
database = client.db("avusd-data-collection");
|
|
||||||
dataCollection = database.collection("records");
|
|
||||||
|
|
||||||
// This code only runs on the server
|
// let results = Meteor.Records.find({deviceId: "1e3e99ef-adf4-4aa2-8784-205bc60f0ce3"}).fetch();
|
||||||
Meteor.publish('chromebookData', function(deviceId) {
|
// console.log(results);
|
||||||
check(deviceId, String);
|
|
||||||
|
|
||||||
return dataCollection.find({deviceId});
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Meteor.methods({
|
if (Meteor.isServer) {
|
||||||
// 'tasks.setChecked'(taskId, setChecked) {
|
Meteor.methods({
|
||||||
// check(taskId, String);
|
/**
|
||||||
// check(setChecked, Boolean);
|
* Collects Chromebook history given one of the possible parameters.
|
||||||
//
|
* @param params An object with a single attribute. The attribute must be one of: deviceId, serial, email. It will find all Chromebook data that starts with the given attribute value.
|
||||||
// const task = Tasks.findOne(taskId);
|
* @returns {any} Array of Chromebook data objects.
|
||||||
// if (task.private && task.owner !== this.userId) {
|
*/
|
||||||
// // If the task is private, make sure only the owner can check it off
|
'DataCollection.chromebookData'(params) {
|
||||||
// throw new Meteor.Error('not-authorized');
|
if(Roles.userIsInRole(Meteor.userId(), "laptop-management", {anyScope:true})) {
|
||||||
// }
|
let query = {};
|
||||||
//
|
|
||||||
// Tasks.update(taskId, { $set: { checked: setChecked } });
|
if (params.deviceId) query.deviceId = params.regex ? {
|
||||||
// },
|
$regex: params.deviceId,
|
||||||
// 'tasks.setPrivate'(taskId, setToPrivate) {
|
$options: "i"
|
||||||
// check(taskId, String);
|
} : params.deviceId;
|
||||||
// check(setToPrivate, Boolean);
|
else if (params.serial) query.serial = params.regex ? {
|
||||||
//
|
$regex: params.serial,
|
||||||
// const task = Tasks.findOne(taskId);
|
$options: "i"
|
||||||
//
|
} : params.serial;
|
||||||
// // Make sure only the task owner can make a task private
|
else if (params.email) query.email = params.regex ? {
|
||||||
// if (task.owner !== this.userId) {
|
$regex: params.email,
|
||||||
// throw new Meteor.Error('not-authorized');
|
$options: "i"
|
||||||
// }
|
} : params.email;
|
||||||
//
|
|
||||||
// Tasks.update(taskId, { $set: { private: setToPrivate } });
|
// console.log("Collecting Chromebook Data: ");
|
||||||
// },
|
// console.log(query);
|
||||||
});
|
let result = Meteor.Records.find(query).fetch();
|
||||||
|
// console.log("Found: ");
|
||||||
|
// console.log(result);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
else {return null;}
|
||||||
|
}
|
||||||
|
// 'tasks.setChecked'(taskId, setChecked) {
|
||||||
|
// check(taskId, String);
|
||||||
|
// check(setChecked, Boolean);
|
||||||
|
//
|
||||||
|
// const task = Tasks.findOne(taskId);
|
||||||
|
// if (task.private && task.owner !== this.userId) {
|
||||||
|
// // If the task is private, make sure only the owner can check it off
|
||||||
|
// throw new Meteor.Error('not-authorized');
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// Tasks.update(taskId, { $set: { checked: setChecked } });
|
||||||
|
// },
|
||||||
|
// 'tasks.setPrivate'(taskId, setToPrivate) {
|
||||||
|
// check(taskId, String);
|
||||||
|
// check(setToPrivate, Boolean);
|
||||||
|
//
|
||||||
|
// const task = Tasks.findOne(taskId);
|
||||||
|
//
|
||||||
|
// // Make sure only the task owner can make a task private
|
||||||
|
// if (task.owner !== this.userId) {
|
||||||
|
// throw new Meteor.Error('not-authorized');
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// Tasks.update(taskId, { $set: { private: setToPrivate } });
|
||||||
|
// },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,2 +1,3 @@
|
|||||||
import "./records.js";
|
|
||||||
import "./users.js";
|
import "./users.js";
|
||||||
|
import "./data-collection.js";
|
||||||
|
import "./admin.js";
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import { Meteor } from 'meteor/meteor';
|
import { Meteor } from 'meteor/meteor';
|
||||||
|
import { Roles } from 'meteor/alanning:roles';
|
||||||
|
import { check } from 'meteor/check';
|
||||||
|
|
||||||
if (Meteor.isServer) {
|
if (Meteor.isServer) {
|
||||||
Meteor.publish(null, function() {
|
Meteor.publish(null, function() {
|
||||||
@@ -9,14 +10,59 @@ if (Meteor.isServer) {
|
|||||||
else {
|
else {
|
||||||
this.ready();
|
this.ready();
|
||||||
}
|
}
|
||||||
})
|
});
|
||||||
|
|
||||||
// Meteor.methods({
|
Meteor.publish(null, function() {
|
||||||
// 'users.setupInitialRoles'() {
|
return Meteor.roles.find({});
|
||||||
// Roles.createRole('admin');
|
});
|
||||||
// Roles.createRole('laptop-management');
|
|
||||||
// Roles.addRolesToParent('laptop-management', 'admin');
|
Meteor.publish("allUsers", function() {
|
||||||
// Roles.addUsersToRoles("zwbMiaSKHix4bWQ8d", 'admin', 'global');
|
// console.log(Meteor.isServer);
|
||||||
// }
|
// console.log("AllUsers");
|
||||||
// });
|
// console.log("Meteor.userId(): " + Meteor.userId());
|
||||||
|
// // console.log(Roles.userIsInRole(Meteor.userId(), "laptop-management"));
|
||||||
|
// console.log(Meteor.roleAssignment.find({ 'user._id': Meteor.userId() }).fetch());
|
||||||
|
// console.log(Roles.userIsInRole(Meteor.user(), "admin", {anyScope:true}));
|
||||||
|
|
||||||
|
// Note: For some reason the {anyScope: true} is necessary on the server for the function to actually check roles.
|
||||||
|
if(Roles.userIsInRole(Meteor.userId(), "admin", {anyScope:true})) {
|
||||||
|
//console.log(Meteor.users.find({}).fetch());
|
||||||
|
return Meteor.users.find({});
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
Meteor.publish("allRoleAssignments", function() {
|
||||||
|
// Note: For some reason the {anyScope: true} is necessary on the server for the function to actually check roles.
|
||||||
|
if(Roles.userIsInRole(Meteor.userId(), "admin", {anyScope:true})) {
|
||||||
|
return Meteor.roleAssignment.find({});
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Meteor.methods({
|
||||||
|
'users.setUserRoles'(userId, roles) {
|
||||||
|
if(Roles.userIsInRole(Meteor.userId(), "admin", {anyScope:true})) {
|
||||||
|
check(userId, String);
|
||||||
|
check(roles, Array);
|
||||||
|
Roles.setUserRoles(userId, roles, {anyScope: true});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
// 'tasks.setPrivate'(taskId, setToPrivate) {
|
||||||
|
// check(taskId, String);
|
||||||
|
// check(setToPrivate, Boolean);
|
||||||
|
//
|
||||||
|
// const task = Tasks.findOne(taskId);
|
||||||
|
//
|
||||||
|
// // Make sure only the task owner can make a task private
|
||||||
|
// if (task.owner !== this.userId) {
|
||||||
|
// throw new Meteor.Error('not-authorized');
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// Tasks.update(taskId, { $set: { private: setToPrivate } });
|
||||||
|
// },
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,13 +1,70 @@
|
|||||||
import { Accounts } from 'meteor/accounts-base'
|
import { Accounts } from 'meteor/accounts-base'
|
||||||
|
import { Roles } from 'meteor/alanning:roles'
|
||||||
|
import {Meteor} from "meteor/meteor";
|
||||||
|
|
||||||
if(Meteor.isCLient) {
|
if(Meteor.isClient) {
|
||||||
Accounts.ui.config({
|
Accounts.ui.config({
|
||||||
passwordSignupFields: 'USERNAME_ONLY'
|
passwordSignupFields: 'USERNAME_ONLY'
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
Accounts.config({
|
Accounts.config({
|
||||||
|
// Allow only certain email domains.
|
||||||
restrictCreationByEmailDomain: function(address) {
|
restrictCreationByEmailDomain: function(address) {
|
||||||
return new RegExp('.*@avpanthers.org$', 'i').test(address)
|
let pattern = process.env.EMAIL_REGEX;
|
||||||
|
|
||||||
|
return new RegExp(pattern, 'i').test(address)
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if(Meteor.isServer) {
|
||||||
|
let adminEmail = process.env.ADMIN_EMAIL;
|
||||||
|
let watchForAdmin = false;
|
||||||
|
|
||||||
|
//Setup the roles.
|
||||||
|
Roles.createRole('admin', {unlessExists: true});
|
||||||
|
Roles.createRole('laptop-management', {unlessExists: true});
|
||||||
|
Roles.addRolesToParent('laptop-management', 'admin', {unlessExists: true});
|
||||||
|
//Roles.addUsersToRoles("zwbMiaSKHix4bWQ8d", 'admin', 'global', {unlessExists: true});
|
||||||
|
|
||||||
|
// If we are passed an email address that should be admin by default, then ensure that user is admin, or mark it as needing to be admin if the user ever logs in.
|
||||||
|
// Given that this app requires Google OAuth2, and we expect logins to be restricted to district email addresses, this should be very secure.
|
||||||
|
if(adminEmail) {
|
||||||
|
let user = Meteor.users.findOne({"services.google.email": adminEmail});
|
||||||
|
|
||||||
|
if(user) {
|
||||||
|
let assignment = Meteor.roleAssignment.findOne({'user._id': user._id, "role._id": "admin"});
|
||||||
|
|
||||||
|
// console.log("Admin Role Assignment: " + JSON.stringify(assignment));
|
||||||
|
if(!assignment) {
|
||||||
|
Roles.addUsersToRoles(user._id, ['admin']);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
watchForAdmin = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Listen for users logging in so we can setup the admin user automatically once they log in the first time.
|
||||||
|
if(watchForAdmin) {
|
||||||
|
// TODO: It would be nice to remove this handler after the admin user is found, but the docs are pretty ambiguous about how to do that. Not a big deal, just annoying.
|
||||||
|
Accounts.onLogin(function (data) {
|
||||||
|
// console.log("User logged in:");
|
||||||
|
// console.log(data.user.services.google.email);
|
||||||
|
|
||||||
|
// data.user == Meteor.user()
|
||||||
|
|
||||||
|
//console.log(JSON.stringify(Meteor.user()));
|
||||||
|
if (watchForAdmin) {
|
||||||
|
try {
|
||||||
|
if (data.user.services.google.email === adminEmail) {
|
||||||
|
Roles.addUsersToRoles(data.user._id, ['admin']);
|
||||||
|
watchForAdmin = false;
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
console.log(err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
8
imports/ui/Admin.svelte
Normal file
8
imports/ui/Admin.svelte
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
<script>
|
||||||
|
import {Meteor} from "meteor/meteor";
|
||||||
|
|
||||||
|
const fixRecords = () => {Meteor.call("admin.fixRecords");}
|
||||||
|
</script>
|
||||||
|
<div class="container">
|
||||||
|
<button type="button" on:click={fixRecords}>Fix Records</button>
|
||||||
|
</div>
|
||||||
26
imports/ui/Announcer.svelte
Normal file
26
imports/ui/Announcer.svelte
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
<!-- Announcer.svelte - From the tinro docs: https://github.com/AlexxNB/tinro -->
|
||||||
|
<!-- This will be included in the App.svelte so that URL changes are announced in a way that accessibility tools can read the page. -->
|
||||||
|
<script>
|
||||||
|
import { router } from 'tinro';
|
||||||
|
$: current = $router.path === '/' ? 'Home' : $router.path.slice(1);
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div aria-live="assertive" aria-atomic="true">
|
||||||
|
{#key current}
|
||||||
|
Navigated to {current}
|
||||||
|
{/key}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
div {
|
||||||
|
position: absolute;
|
||||||
|
left: 0;
|
||||||
|
top: 0;
|
||||||
|
clip: rect(0 0 0 0);
|
||||||
|
clip-path: inset(50%);
|
||||||
|
overflow: hidden;
|
||||||
|
white-space: nowrap;
|
||||||
|
width: 1px;
|
||||||
|
height: 1px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -1,269 +1,242 @@
|
|||||||
|
|
||||||
|
<script>
|
||||||
|
import {Meteor} from "meteor/meteor";
|
||||||
|
import {Route, router} from 'tinro';
|
||||||
|
import {onMount} from 'svelte';
|
||||||
|
import {useTracker} from 'meteor/rdb:svelte-meteor-data';
|
||||||
|
import {Roles} from 'meteor/alanning:roles';
|
||||||
|
import Chromebooks from './Chromebooks.svelte';
|
||||||
|
import Users from './Users.svelte';
|
||||||
|
import TestTable from './TestTable.svelte';
|
||||||
|
import ListUsers from './ListUsers.svelte';
|
||||||
|
import Admin from './Admin.svelte';
|
||||||
|
import Announcer from './Announcer.svelte';
|
||||||
|
import {BlazeTemplate} from 'meteor/svelte:blaze-integration';
|
||||||
|
import ServiceConfiguration from "meteor/service-configuration";
|
||||||
|
|
||||||
|
// When the URL changes, run the code... in this case to scroll to the top.
|
||||||
|
router.subscribe(_ => window.scrollTo(0, 0));
|
||||||
|
|
||||||
|
// onMount(async () => {
|
||||||
|
// // Meteor.subscribe('records');
|
||||||
|
// });
|
||||||
|
|
||||||
|
// $: incompleteCount = useTracker(() => Tasks.find({checked: {$ne: true}}).count());
|
||||||
|
|
||||||
|
$: currentUser = useTracker(() => Meteor.user());
|
||||||
|
$: canManageLaptops = false;
|
||||||
|
$: isAdmin = false;
|
||||||
|
|
||||||
|
Tracker.autorun(() => {
|
||||||
|
// For some reason currentUser is always null here, and is not reactive (user changes and this does not get re-called).
|
||||||
|
let user = Meteor.user();
|
||||||
|
canManageLaptops = user && Roles.userIsInRole(user._id, 'laptop-management', 'global');
|
||||||
|
isAdmin = user && Roles.userIsInRole(user._id, 'admin', 'global');
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
// const taskStore = Tasks.find({}, {sort: {createdAt: -1}});
|
||||||
|
// $: {
|
||||||
|
// tasks = $taskStore;
|
||||||
|
// if (hideCompleted) {
|
||||||
|
// tasks = tasks.filter(task => !task.checked);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// function handleSubmit(event) {
|
||||||
|
// Meteor.call("tasks.insert", newTask);
|
||||||
|
// // Clear form
|
||||||
|
// newTask = "";
|
||||||
|
// }
|
||||||
|
|
||||||
|
function performLogin() {
|
||||||
|
//Login style can be "popup" or "redirect". I am not sure we need to request and offline token.
|
||||||
|
Meteor.loginWithGoogle({loginStyle: "popup", requestOfflineToken: true}, (err) => {
|
||||||
|
if (err) {
|
||||||
|
console.log(err);
|
||||||
|
} else {
|
||||||
|
//console.log("Logged in");
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function performLogout() {
|
||||||
|
Meteor.logout();
|
||||||
|
}
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<Announcer/>
|
||||||
|
|
||||||
|
<div class="container">
|
||||||
|
<header class="row">
|
||||||
|
<div class="col-12 logoContainer">
|
||||||
|
<img class="logo" src="/images/logo.svg"/>
|
||||||
|
<div class="login">
|
||||||
|
{#if !$currentUser}
|
||||||
|
<button type="button" role="button" on:click={performLogin}>Login</button>
|
||||||
|
{:else}
|
||||||
|
<button type="button" role="button" on:click={performLogout}>Logout</button>
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-12 center" style="margin-bottom: 0"><h1 style="margin-bottom: 0">District Central</h1></div>
|
||||||
|
<div class="col-12 center">
|
||||||
|
<div class="nav-separator"></div>
|
||||||
|
</div>
|
||||||
|
<nav class="col-12 center">
|
||||||
|
<a href="/">Home</a>
|
||||||
|
{#if canManageLaptops}
|
||||||
|
<a href="/chromebooks">Chromebooks</a>
|
||||||
|
{/if}
|
||||||
|
{#if canManageLaptops}
|
||||||
|
<a href="/users">Users</a>
|
||||||
|
{/if}
|
||||||
|
{#if isAdmin}
|
||||||
|
<a href="/admin">Admin</a>
|
||||||
|
{/if}
|
||||||
|
<!-- <a href="/TestTable">Test</a>-->
|
||||||
|
<!-- <a href="/ListUsers">List Users</a>-->
|
||||||
|
</nav>
|
||||||
|
</header>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<Route path="/">
|
||||||
|
<div class="container">
|
||||||
|
<div class="row">
|
||||||
|
TODO: Some statistics and such.
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Route>
|
||||||
|
<Route path="/ListUsers">
|
||||||
|
<!-- <ListUsers/>-->
|
||||||
|
</Route>
|
||||||
|
<Route path="/admin">
|
||||||
|
{#if isAdmin}
|
||||||
|
<Admin/>
|
||||||
|
{/if}
|
||||||
|
</Route>
|
||||||
|
<Route path="/TestTable/*">
|
||||||
|
<!-- <TestTable/>-->
|
||||||
|
</Route>
|
||||||
|
<Route path="/chromebooks/*">
|
||||||
|
{#if canManageLaptops}
|
||||||
|
<Chromebooks/>
|
||||||
|
{:else}
|
||||||
|
<!-- User not authorized to use this UI. Don't render anything because it is likely the user is still loading and will have access in a moment. -->
|
||||||
|
{/if}
|
||||||
|
</Route>
|
||||||
|
<Route path="/users/*">
|
||||||
|
{#if isAdmin}
|
||||||
|
<Users/>
|
||||||
|
{:else}
|
||||||
|
<!-- User not authorized to use this UI. Don't render anything because it is likely the user is still loading and will have access in a moment. -->
|
||||||
|
{/if}
|
||||||
|
</Route>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
nav {
|
@font-face {
|
||||||
font-size: 2rem;
|
font-family: 'KaushanScript-Regular';
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 400;
|
||||||
|
src: url('/fonts/KaushanScript-Regular.ttf') format('truetype');
|
||||||
}
|
}
|
||||||
a {
|
|
||||||
display: block;
|
.nav-separator {
|
||||||
color: green;
|
height: 0.2rem;
|
||||||
|
width: 40%;
|
||||||
|
margin-left: 30%;
|
||||||
|
background-color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
nav {
|
||||||
|
font-size: 1.4rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
nav a {
|
||||||
|
display: inline-block;
|
||||||
|
color: #a6a6ea;
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
nav a:hover {
|
||||||
|
color: #6363ee;
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
|
||||||
|
nav a + a {
|
||||||
|
margin-left: 2rem;
|
||||||
|
}
|
||||||
|
|
||||||
html {
|
html {
|
||||||
background-color: #000121;
|
background-color: #000121;
|
||||||
font-family: 'Roboto', sans-serif;
|
font-family: 'Roboto', sans-serif;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Forbidden CSS */
|
header {
|
||||||
.maincontainer {
|
background: #2c031c;
|
||||||
position: relative;
|
|
||||||
top: -50px;
|
|
||||||
transform: scale(0.8);
|
|
||||||
background: url("/public/images/forbidden/HauntedHouseBackground.png");
|
|
||||||
background-repeat: no-repeat;
|
|
||||||
background-position: center;
|
|
||||||
background-size: 700px 600px;
|
|
||||||
width: 800px;
|
|
||||||
height: 600px;
|
|
||||||
margin: 0px auto;
|
|
||||||
display: grid;
|
|
||||||
}
|
|
||||||
.foregroundimg {
|
|
||||||
position: relative;
|
|
||||||
width: 100%;
|
|
||||||
top: -230px;
|
|
||||||
z-index: 5;
|
|
||||||
}
|
|
||||||
.errorcode {
|
|
||||||
position: relative;
|
|
||||||
top: -200px;
|
|
||||||
font-family: 'Creepster', cursive;
|
|
||||||
color: white;
|
color: white;
|
||||||
text-align: center;
|
/*background-image: linear-gradient(to bottom, #d0edf5, #e1e5f0 100%);*/
|
||||||
font-size: 6em;
|
padding: 20px 15px 15px 15px;
|
||||||
letter-spacing: 0.1em;
|
|
||||||
}
|
|
||||||
.errortext {
|
|
||||||
position: relative;
|
position: relative;
|
||||||
top: -260px;
|
|
||||||
color: #FBD130;
|
|
||||||
text-align: center;
|
|
||||||
text-transform: uppercase;
|
|
||||||
font-size: 1.8em;
|
|
||||||
}
|
}
|
||||||
.bat {
|
|
||||||
opacity: 0;
|
|
||||||
position: relative;
|
|
||||||
transform-origin: center;
|
|
||||||
z-index: 3;
|
|
||||||
}
|
|
||||||
.bat:nth-child(1) {
|
|
||||||
top: 380px;
|
|
||||||
left: 120px;
|
|
||||||
transform: scale(0.5);
|
|
||||||
animation: 13s 1s flyBat1 infinite linear;
|
|
||||||
}
|
|
||||||
.bat:nth-child(2) {
|
|
||||||
top: 280px;
|
|
||||||
left: 80px;
|
|
||||||
transform: scale(0.3);
|
|
||||||
animation: 8s 4s flyBat2 infinite linear;
|
|
||||||
}
|
|
||||||
.bat:nth-child(3) {
|
|
||||||
top: 200px;
|
|
||||||
left: 150px;
|
|
||||||
transform: scale(0.4);
|
|
||||||
animation: 12s 2s flyBat3 infinite linear;
|
|
||||||
}
|
|
||||||
.body {
|
|
||||||
position: relative;
|
|
||||||
width: 50px;
|
|
||||||
top: 12px;
|
|
||||||
}
|
|
||||||
.wing {
|
|
||||||
width: 150px;
|
|
||||||
position: relative;
|
|
||||||
transform-origin: right center;
|
|
||||||
}
|
|
||||||
.leftwing {
|
|
||||||
left: 30px;
|
|
||||||
animation: 0.8s flapLeft infinite ease-in-out;
|
|
||||||
}
|
|
||||||
.rightwing {
|
|
||||||
left: -180px;
|
|
||||||
transform: scaleX(-1);
|
|
||||||
animation: 0.8s flapRight infinite ease-in-out;
|
|
||||||
}
|
|
||||||
@keyframes flapLeft {
|
|
||||||
0% { transform: rotateZ(0); }
|
|
||||||
50% { transform: rotateZ(10deg) rotateY(40deg); }
|
|
||||||
100% { transform: rotateZ(0); }
|
|
||||||
}
|
|
||||||
@keyframes flapRight {
|
|
||||||
0% { transform: scaleX(-1) rotateZ(0); }
|
|
||||||
50% { transform: scaleX(-1) rotateZ(10deg) rotateY(40deg); }
|
|
||||||
100% { transform: scaleX(-1) rotateZ(0); }
|
|
||||||
}
|
|
||||||
@keyframes flyBat1 {
|
|
||||||
0% { opacity: 1; transform: scale(0.5)}
|
|
||||||
25% { opacity: 1; transform: scale(0.5) translate(-400px, -330px) }
|
|
||||||
50% { opacity: 1; transform: scale(0.5) translate(400px, -800px) }
|
|
||||||
75% { opacity: 1; transform: scale(0.5) translate(600px, 100px) }
|
|
||||||
100% { opacity: 1; transform: scale(0.5) translate(100px, 300px) }
|
|
||||||
}
|
|
||||||
@keyframes flyBat2 {
|
|
||||||
0% { opacity: 1; transform: scale(0.3)}
|
|
||||||
25% { opacity: 1; transform: scale(0.3) translate(200px, -330px) }
|
|
||||||
50% { opacity: 1; transform: scale(0.3) translate(-300px, -800px) }
|
|
||||||
75% { opacity: 1; transform: scale(0.3) translate(-400px, 100px) }
|
|
||||||
100% { opacity: 1; transform: scale(0.3) translate(100px, 300px) }
|
|
||||||
}
|
|
||||||
@keyframes flyBat3 {
|
|
||||||
0% { opacity: 1; transform: scale(0.4)}
|
|
||||||
25% { opacity: 1; transform: scale(0.4) translate(-350px, -330px) }
|
|
||||||
50% { opacity: 1; transform: scale(0.4) translate(400px, -800px) }
|
|
||||||
75% { opacity: 1; transform: scale(0.4) translate(-600px, 100px) }
|
|
||||||
100% { opacity: 1; transform: scale(0.4) translate(100px, 300px) }
|
|
||||||
}
|
|
||||||
/*@media only screen and (max-width: 850px) {
|
|
||||||
.maincontainer {
|
|
||||||
transform: scale(0.6);
|
|
||||||
width: 600px;
|
|
||||||
height: 400px;
|
|
||||||
background-size: 600px 400px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.errortext {
|
h1 {
|
||||||
font-size: 1em;
|
color: white;
|
||||||
}
|
font-family: "KaushanScript-Regular", sans-serif;
|
||||||
}*/
|
font-size: 4rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.logo {
|
||||||
|
position: absolute;
|
||||||
|
left: 0;
|
||||||
|
top: 0;
|
||||||
|
width: 8rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.logoContainer {
|
||||||
|
height: 0;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.login {
|
||||||
|
position: absolute;
|
||||||
|
right: 0;
|
||||||
|
top: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.login button {
|
||||||
|
background: #7171ec;
|
||||||
|
border-radius: 999px;
|
||||||
|
box-shadow: #5E5DF0 0 10px 20px -10px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
color: #FFFFFF;
|
||||||
|
cursor: pointer;
|
||||||
|
font-family: Inter, Helvetica, "Apple Color Emoji", "Segoe UI Emoji", NotoColorEmoji, "Noto Color Emoji", "Segoe UI Symbol", "Android Emoji", EmojiSymbols, -apple-system, system-ui, "Segoe UI", Roboto, "Helvetica Neue", "Noto Sans", sans-serif;
|
||||||
|
font-weight: 700;
|
||||||
|
line-height: 24px;
|
||||||
|
opacity: 1;
|
||||||
|
outline: 0 solid transparent;
|
||||||
|
padding: 8px 18px;
|
||||||
|
user-select: none;
|
||||||
|
-webkit-user-select: none;
|
||||||
|
touch-action: manipulation;
|
||||||
|
width: fit-content;
|
||||||
|
word-break: break-word;
|
||||||
|
border: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 600px) {
|
||||||
|
.logo {
|
||||||
|
position: absolute;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
top: 0;
|
||||||
|
margin: 0 auto;
|
||||||
|
width: 8rem;
|
||||||
|
}
|
||||||
|
.logoContainer {
|
||||||
|
height: 6rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<script>
|
|
||||||
import {Meteor} from "meteor/meteor";
|
|
||||||
import {Route} from 'tinro';
|
|
||||||
import {onMount} from 'svelte';
|
|
||||||
import {useTracker} from 'meteor/rdb:svelte-meteor-data';
|
|
||||||
import {Roles} from 'meteor/alanning:roles';
|
|
||||||
import Chromebooks from './Chromebooks.svelte';
|
|
||||||
import {BlazeTemplate} from 'meteor/svelte:blaze-integration';
|
|
||||||
import {Records} from '../api/records.js'
|
|
||||||
import ServiceConfiguration from "meteor/service-configuration";
|
|
||||||
|
|
||||||
//import './imports/ui/App.css';
|
|
||||||
|
|
||||||
//let currentUser;
|
|
||||||
|
|
||||||
onMount(async () => {
|
|
||||||
// Meteor.subscribe('records');
|
|
||||||
});
|
|
||||||
|
|
||||||
// $: incompleteCount = useTracker(() => Tasks.find({checked: {$ne: true}}).count());
|
|
||||||
|
|
||||||
$: currentUser = useTracker(() => Meteor.user());
|
|
||||||
$: isAdmin = useTracker(() => Roles.userIsInRole(currentUser._id, 'laptop-management', 'global'));
|
|
||||||
|
|
||||||
// Tracker.autorun(() => {
|
|
||||||
// let user = Meteor.user();
|
|
||||||
// let isManagement = user ? Roles.userIsInRole(user._id, 'laptop-management', 'global') : 0;
|
|
||||||
//
|
|
||||||
// if(user && isManagement) {
|
|
||||||
// new ProcessLaptops({
|
|
||||||
// target: document.getElementById('appView')
|
|
||||||
// });
|
|
||||||
// }
|
|
||||||
// });
|
|
||||||
|
|
||||||
// const taskStore = Tasks.find({}, {sort: {createdAt: -1}});
|
|
||||||
// $: {
|
|
||||||
// tasks = $taskStore;
|
|
||||||
// if (hideCompleted) {
|
|
||||||
// tasks = tasks.filter(task => !task.checked);
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
// function handleSubmit(event) {
|
|
||||||
// Meteor.call("tasks.insert", newTask);
|
|
||||||
// // Clear form
|
|
||||||
// newTask = "";
|
|
||||||
// }
|
|
||||||
|
|
||||||
function performLogin() {
|
|
||||||
//Login style can be "popup" or "redirect". I am not sure we need to request and offline token.
|
|
||||||
Meteor.loginWithGoogle({loginStyle: "popup", requestOfflineToken: true}, (err) => {
|
|
||||||
if(err) {
|
|
||||||
console.log(err);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
//console.log("Logged in");
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
function performLogout() {
|
|
||||||
Meteor.logout();
|
|
||||||
}
|
|
||||||
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<Route path="/">
|
|
||||||
<div class="container">
|
|
||||||
<header class="row">
|
|
||||||
<nav class="col-12 center">
|
|
||||||
<h1>AVUSD District Central</h1>
|
|
||||||
<a href="/">Home</a>
|
|
||||||
{#if isAdmin}
|
|
||||||
<a href="/chromebooks">Chromebooks</a>
|
|
||||||
{/if}
|
|
||||||
</nav>
|
|
||||||
{#if !$currentUser}
|
|
||||||
<button type="button" on:click={performLogin}>Login</button>
|
|
||||||
{:else}
|
|
||||||
<button type="button" on:click={performLogout}>Logout</button>
|
|
||||||
{/if}
|
|
||||||
</header>
|
|
||||||
</div>
|
|
||||||
</Route>
|
|
||||||
{#if $currentUser}
|
|
||||||
{#if isAdmin}
|
|
||||||
<Route path="/chromebooks/*">
|
|
||||||
<Chromebooks/>
|
|
||||||
</Route>
|
|
||||||
{:else}
|
|
||||||
<Route fallback redirect="/"/>
|
|
||||||
{/if}
|
|
||||||
{:else}
|
|
||||||
<div className="container" style="background-color: #4d4242">
|
|
||||||
<div class="maincontainer row">
|
|
||||||
<div class="bat">
|
|
||||||
<img class="wing leftwing"
|
|
||||||
src="/images/forbidden/bat-wing.png">
|
|
||||||
<img class="body"
|
|
||||||
src="/images/forbidden/bat-body.png" alt="bat">
|
|
||||||
<img class="wing rightwing"
|
|
||||||
src="/images/forbidden/bat-wing.png">
|
|
||||||
</div>
|
|
||||||
<div class="bat">
|
|
||||||
<img class="wing leftwing"
|
|
||||||
src="/images/forbidden/bat-wing.png">
|
|
||||||
<img class="body"
|
|
||||||
src="/images/forbidden/bat-body.png" alt="bat">
|
|
||||||
<img class="wing rightwing"
|
|
||||||
src="/images/forbidden/bat-wing.png">
|
|
||||||
</div>
|
|
||||||
<div class="bat">
|
|
||||||
<img class="wing leftwing"
|
|
||||||
src="/images/forbidden/bat-wing.png">
|
|
||||||
<img class="body"
|
|
||||||
src="/images/forbidden/bat-body.png" alt="bat">
|
|
||||||
<img class="wing rightwing"
|
|
||||||
src="/images/forbidden/bat-wing.png">
|
|
||||||
</div>
|
|
||||||
<img class="foregroundimg" src="/images/forbidden/HauntedHouseForeground.png" alt="haunted house">
|
|
||||||
|
|
||||||
</div>
|
|
||||||
<h1 class="errorcode">ERROR 403</h1>
|
|
||||||
<div class="errortext">This area is forbidden. Turn back now!</div>
|
|
||||||
</div>
|
|
||||||
{/if}
|
|
||||||
|
|||||||
@@ -1,41 +1,103 @@
|
|||||||
<style>
|
<style>
|
||||||
.error {
|
.error {
|
||||||
color: darkred;
|
color: darkred;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
<script>
|
<script>
|
||||||
|
import {router} from 'tinro';
|
||||||
import {Html5QrcodeScanner} from "html5-qrcode";
|
import {Html5QrcodeScanner} from "html5-qrcode";
|
||||||
|
import {Html5Qrcode} from "html5-qrcode";
|
||||||
|
import {useTracker} from "meteor/rdb:svelte-meteor-data";
|
||||||
|
import {Meteor} from "meteor/meteor";
|
||||||
|
import { Session } from 'meteor/session';
|
||||||
|
|
||||||
function onScanSuccess(decodedText, decodedResult) {
|
let c = 0;
|
||||||
console.log('Code matched ' + decodedResult);
|
|
||||||
document.getElementById("log").prepend(decodedText);
|
$: deviceId = null;
|
||||||
|
$: chromebookData = [];
|
||||||
|
$: {
|
||||||
|
if (deviceId) {
|
||||||
|
Meteor.call("DataCollection.chromebookData", deviceId, (error, result) => {
|
||||||
|
// console.log("Call returned");
|
||||||
|
if (error) {
|
||||||
|
console.error(error);
|
||||||
|
} else {
|
||||||
|
chromebookData = result;
|
||||||
|
// console.log("result: " + result);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
chromebookData = [];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function onScanFailure(error) {
|
function fakeScan() {
|
||||||
|
setTimeout(() => {
|
||||||
|
let decodedText = "1e3e99ef-adf4-4aa2-8784-205bc60f0ce3";
|
||||||
|
|
||||||
|
//deviceId = decodedText;
|
||||||
|
//window.location.href="/chromebooks/byDevice/" + encodeURIComponent(decodedText);
|
||||||
|
//router.location.goto("/chromebooks/byDevice/" + encodeURIComponent(decodedText));
|
||||||
|
router.goto("/chromebooks?deviceId=" + encodeURIComponent(decodedText));
|
||||||
|
}, 1000);
|
||||||
}
|
}
|
||||||
|
function clear() {
|
||||||
|
deviceId = "";
|
||||||
|
}
|
||||||
|
|
||||||
|
function setDeviceId(id) {
|
||||||
|
deviceId = id;
|
||||||
|
}
|
||||||
|
|
||||||
|
let html5QrCode;
|
||||||
|
|
||||||
function scanner() {
|
function scanner() {
|
||||||
let html5QrcodeScanner = new Html5QrcodeScanner("reader", {
|
html5QrCode = new Html5Qrcode("reader");
|
||||||
fps: 10,
|
const config = {fps: 10, qrbox: {width: 250, height: 250}};
|
||||||
qrbox: {width: 250, height: 250}
|
html5QrCode.start({facingMode: "environment"}, config, (decodedText, decodedResult) => {
|
||||||
}, /* verbose */ false);
|
//console.log('Code matched ' + decodedText);
|
||||||
|
//document.getElementById("log").prepend(JSON.stringify(decodedResult));
|
||||||
|
//setDeviceId(decodedText);
|
||||||
|
window.location.href="/chromebooks?deviceId=" + encodeURIComponent(decodedText);
|
||||||
|
|
||||||
html5QrcodeScanner.render(onScanSuccess, onScanFailure);
|
// Stop Scanning
|
||||||
|
html5QrCode.stop().then((ignore) => {
|
||||||
|
// QR Code scanning is stopped.
|
||||||
|
}).catch((err) => {
|
||||||
|
// Stop failed, handle it.
|
||||||
|
});
|
||||||
|
}, (error) => {
|
||||||
|
//TODO:
|
||||||
|
});
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<div class="container">
|
||||||
<div className="container">
|
<div class="row col-12" style="margin-bottom: 1rem">
|
||||||
<div class="row">
|
<button type="button" on:click={fakeScan}>Fake Scan</button>
|
||||||
Hello World!
|
<button type="button" on:click={clear}>Clear</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="row">
|
<!-- <div class="row">-->
|
||||||
<div use:scanner id="reader" width="300px"></div>
|
<!-- <div use:scanner id="reader" width="300px"></div>-->
|
||||||
|
<!-- </div>-->
|
||||||
|
<div class="row col-12">
|
||||||
|
<a href="#" on:click={scanner}>Scan</a>
|
||||||
|
<!-- <button type="button" on:click={scanner}>Scan</button>-->
|
||||||
|
<div id="reader" width="250px"></div>
|
||||||
</div>
|
</div>
|
||||||
|
<br/>
|
||||||
<div class="row">
|
<div class="row col-12">
|
||||||
<div id="log" class="col-12">
|
<div id="log" class="col-12">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="row col-12">
|
||||||
|
<ul>
|
||||||
|
{#each chromebookData as data}
|
||||||
|
<li>{data.email}<br/>{data.serial}<br/>{new Date(data.startTime).toLocaleDateString("en-US") + "-" + new Date(data.endTime).toLocaleDateString("en-US")}</li>
|
||||||
|
{/each}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,32 +1,187 @@
|
|||||||
|
|
||||||
<style>
|
<style>
|
||||||
nav {
|
a.button {
|
||||||
font-size: 2rem;
|
background: #7171ec;
|
||||||
|
border-radius: 999px;
|
||||||
|
box-shadow: #5E5DF0 0 10px 20px -10px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
color: #FFFFFF;
|
||||||
|
cursor: pointer;
|
||||||
|
font-family: Inter,Helvetica,"Apple Color Emoji","Segoe UI Emoji",NotoColorEmoji,"Noto Color Emoji","Segoe UI Symbol","Android Emoji",EmojiSymbols,-apple-system,system-ui,"Segoe UI",Roboto,"Helvetica Neue","Noto Sans",sans-serif;
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: 700;
|
||||||
|
line-height: 24px;
|
||||||
|
opacity: 1;
|
||||||
|
outline: 0 solid transparent;
|
||||||
|
padding: 8px 18px;
|
||||||
|
user-select: none;
|
||||||
|
-webkit-user-select: none;
|
||||||
|
touch-action: manipulation;
|
||||||
|
width: fit-content;
|
||||||
|
word-break: break-word;
|
||||||
|
border: 0;
|
||||||
|
text-decoration: none;
|
||||||
}
|
}
|
||||||
a {
|
input {
|
||||||
display: block;
|
--background: #fff;
|
||||||
color: green;
|
--border-default: #D0D0DF;
|
||||||
text-decoration: none;
|
--border-active: #3D6DF9;
|
||||||
|
/*--shadow-default: #{rgba(#202048, .12)};*/
|
||||||
|
/*--shadow-active: #{rgba(#3D6DF9, .25)};*/
|
||||||
|
--text-color: #818190;
|
||||||
|
--placeholder-color: #C9C9D9;
|
||||||
|
--placeholder-color-hover: #BABAC9;
|
||||||
|
--close: #818190;
|
||||||
|
--close-light: #BABAC9;
|
||||||
|
--close-background: #F1F1FA;
|
||||||
|
width: 100%;
|
||||||
|
max-width: 240px;
|
||||||
|
border-radius: 8px;
|
||||||
|
box-shadow: inset 0 0 0 1px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.options {
|
||||||
|
margin: 0 auto;
|
||||||
|
max-width: 800px;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import {Route} from 'tinro';
|
import {Route, router, meta} from 'tinro';
|
||||||
import ChromebookScan from './ChromebookScan.svelte';
|
import ChromebookScan from './ChromebookScan.svelte';
|
||||||
|
import {Meteor} from "meteor/meteor";
|
||||||
|
|
||||||
|
$: serialInput = null;
|
||||||
|
$: emailInput = null;
|
||||||
|
function serialSearch() {
|
||||||
|
router.goto("/chromebooks?serial=" + encodeURIComponent(serialInput) + "®ex=true");
|
||||||
|
}
|
||||||
|
function emailSearch() {
|
||||||
|
router.goto("/chromebooks?email=" + encodeURIComponent(emailInput) + "®ex=true");
|
||||||
|
}
|
||||||
|
|
||||||
|
// console.log("Loading Script");
|
||||||
|
// //Attempt to listen for URL changes (query portion specifically).
|
||||||
|
// (function(history){
|
||||||
|
// const pushState = history.pushState;
|
||||||
|
// history.pushState = function(state) {
|
||||||
|
// if (typeof history.onpushstate == "function") {
|
||||||
|
// history.onpushstate({state: state});
|
||||||
|
// }
|
||||||
|
// // Call your custom function here
|
||||||
|
// console.log("Push state");
|
||||||
|
// console.log(history);
|
||||||
|
// console.log(arguments);
|
||||||
|
// return pushState.apply(history, arguments);
|
||||||
|
// }
|
||||||
|
// })(window.history);
|
||||||
|
|
||||||
|
// const params = Object.fromEntries(new URLSearchParams(window.location.search));
|
||||||
|
//
|
||||||
|
// console.log("Params: ");
|
||||||
|
// console.log(params);
|
||||||
|
|
||||||
|
$: deviceId = null;
|
||||||
|
$: serial = null;
|
||||||
|
$: email = null
|
||||||
|
$: regex = false;
|
||||||
|
$: router.subscribe(query => {
|
||||||
|
deviceId = router.location.query.get("deviceId");
|
||||||
|
serial = router.location.query.get("serial");
|
||||||
|
email = router.location.query.get("email");
|
||||||
|
regex = router.location.query.get("regex");
|
||||||
|
|
||||||
|
if(deviceId) deviceId = decodeURIComponent(deviceId);
|
||||||
|
if(serial) serial = decodeURIComponent(serial);
|
||||||
|
if(email) email = decodeURIComponent(email);
|
||||||
|
if(regex) regex = true;
|
||||||
|
|
||||||
|
// console.log("Query:");
|
||||||
|
// console.log(deviceId);
|
||||||
|
// console.log(serial);
|
||||||
|
// console.log(email);
|
||||||
|
});
|
||||||
|
$: chromebookData = null;
|
||||||
|
$: {
|
||||||
|
if(deviceId || serial || email) {
|
||||||
|
let params = {};
|
||||||
|
|
||||||
|
if(deviceId) params.deviceId = deviceId;
|
||||||
|
else if(serial) params.serial = serial;
|
||||||
|
else if(email) params.email = email;
|
||||||
|
|
||||||
|
if(regex) params.regex = true;
|
||||||
|
|
||||||
|
Meteor.call("DataCollection.chromebookData", params, (error, result) => {
|
||||||
|
if (error) {
|
||||||
|
console.error(error);
|
||||||
|
} else {
|
||||||
|
chromebookData = result;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
chromebookData = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<Route path="/">
|
<Route path="/" let:meta>
|
||||||
<div className="container">
|
{#if chromebookData}
|
||||||
<div class="row">
|
<div class="container">
|
||||||
<nav class="col-12 center">
|
<div class="row col-12">
|
||||||
<h1>Chromebook Management</h1>
|
<ul>
|
||||||
<a href="chromebooks/scan">Scan A Chromebook</a>
|
{#each chromebookData as data}
|
||||||
<a href="chromebooks/byStudent">Chromebook History By Student</a>
|
<li><a href="/chromebooks?email={encodeURIComponent(data.email)}">{data.email}</a><br/>
|
||||||
<a href="chromebooks/byChromebook">Chromebook History By Chromebook</a>
|
<a href="/chromebooks?deviceId={encodeURIComponent(data.deviceId)}">{data.deviceId}</a><br/>
|
||||||
</nav>
|
<a href="/chromebooks?serial={encodeURIComponent(data.serial)}">{data.serial}</a><br/>
|
||||||
|
{new Date(data.startTime).toLocaleDateString("en-US") + "-" + new Date(data.endTime).toLocaleDateString("en-US")}
|
||||||
|
</li>
|
||||||
|
{/each}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{:else}
|
||||||
|
<div class="container">
|
||||||
|
<div class="row col-12">
|
||||||
|
<div class="options">
|
||||||
|
<h1>Chromebook Management</h1>
|
||||||
|
|
||||||
|
<ul>
|
||||||
|
<li>
|
||||||
|
By Chromebook Device ID: <a href="/chromebooks/scan" className="button">Scan</a>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
By Chromebook Serial Number: <br/>
|
||||||
|
<input type="text" bind:value="{serialInput}" placeholder="Serial Number"/><br/>
|
||||||
|
<button type="button" role="button" on:click={serialSearch}>Search</button>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
By Email Address: <br/>
|
||||||
|
<input type="text" bind:value="{emailInput}" placeholder="Email"/>@avpanthers.org<br/>
|
||||||
|
<button type="button" role="button" on:click={emailSearch}>Search</button>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
</Route>
|
||||||
|
|
||||||
|
<Route path="/byDevice/:deviceId">
|
||||||
|
<div class="container">
|
||||||
|
<div class="row col-12">
|
||||||
|
<ul>
|
||||||
|
{#each chromebookData as data}
|
||||||
|
<li>{data.email}<br/>{data.serial}
|
||||||
|
<br/>{new Date(data.startTime).toLocaleDateString("en-US") + "-" + new Date(data.endTime).toLocaleDateString("en-US")}
|
||||||
|
</li>
|
||||||
|
{/each}
|
||||||
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</Route>
|
</Route>
|
||||||
|
|
||||||
<Route path="/scan">
|
<Route path="/scan">
|
||||||
<ChromebookScan/>
|
<ChromebookScan/>
|
||||||
</Route>
|
</Route>
|
||||||
|
|||||||
181
imports/ui/FlexTable.svelte
Normal file
181
imports/ui/FlexTable.svelte
Normal file
@@ -0,0 +1,181 @@
|
|||||||
|
<script>
|
||||||
|
export let rows;
|
||||||
|
export let columns;
|
||||||
|
export let rowKey;
|
||||||
|
export let edited;
|
||||||
|
|
||||||
|
// Setup a width for each column.
|
||||||
|
columns.forEach(column => {
|
||||||
|
let min = column.minWidth ? Math.max(10, column.minWidth) : 10;
|
||||||
|
let weight = column.weight ? Math.max(1, column.weight) : 1;
|
||||||
|
column.width = 'minmax(' + min + 'px, ' + weight + 'fr)';
|
||||||
|
});
|
||||||
|
let gridTemplateColumns = columns.map(({width}) => width).join(' ');
|
||||||
|
|
||||||
|
let headerBeingResized = null;
|
||||||
|
let horizontalScrollOffset = 0;
|
||||||
|
const initResize = ({target}) => {
|
||||||
|
headerBeingResized = target.parentNode;
|
||||||
|
window.addEventListener('mousemove', onMouseMove);
|
||||||
|
window.addEventListener('mouseup', completeResize);
|
||||||
|
headerBeingResized.classList.add('header--being-resized');
|
||||||
|
};
|
||||||
|
const completeResize = () => {
|
||||||
|
window.removeEventListener('mousemove', onMouseMove);
|
||||||
|
window.removeEventListener('mouseup', completeResize);
|
||||||
|
headerBeingResized.classList.remove('header--being-resized');
|
||||||
|
headerBeingResized = null;
|
||||||
|
};
|
||||||
|
const onMouseMove = e => {
|
||||||
|
try {
|
||||||
|
// Calculate the desired width.
|
||||||
|
horizontalScrollOffset = document.documentElement.scrollLeft;
|
||||||
|
let parentX = Math.round(headerBeingResized.getBoundingClientRect().x);
|
||||||
|
const width = horizontalScrollOffset + (e.clientX - parentX);
|
||||||
|
// Update the column object with the new size value.
|
||||||
|
const column = columns.find(({element}) => element === headerBeingResized);
|
||||||
|
column.width = Math.max(column.minWidth, width) + "px";
|
||||||
|
// Ensure all the column widths are converted to fixed sizes.
|
||||||
|
columns.forEach((column, index) => {
|
||||||
|
if((index < columns.length - 1) && (column.width.startsWith('minmax'))) {
|
||||||
|
column.width = parseInt(column.element.clientWidth, 10) + 'px';
|
||||||
|
}
|
||||||
|
});
|
||||||
|
// Render the new column sizes.
|
||||||
|
gridTemplateColumns = columns.map(({width}) => width).join(' ');
|
||||||
|
} catch(e) {console.log(e);}
|
||||||
|
}
|
||||||
|
|
||||||
|
let selectedRowElement = null;
|
||||||
|
const selectRow = (e, row) => {
|
||||||
|
let element = e.target;
|
||||||
|
|
||||||
|
while(element && element.nodeName !== "TR") element = element.parentNode;
|
||||||
|
|
||||||
|
if(selectedRowElement) {
|
||||||
|
selectedRowElement.classList.remove('selected');
|
||||||
|
}
|
||||||
|
|
||||||
|
selectedRowElement = element;
|
||||||
|
element.classList.add('selected');
|
||||||
|
}
|
||||||
|
|
||||||
|
let editorContainer;
|
||||||
|
const editRow = (e, row) => {
|
||||||
|
let element = e.target;
|
||||||
|
while(element && element.nodeName !== "TR") element = element.parentNode;
|
||||||
|
let editor = element.querySelector('.editor');
|
||||||
|
|
||||||
|
// Save the edited row so the editor has access to it.
|
||||||
|
$edited = row;
|
||||||
|
|
||||||
|
if(editor) {
|
||||||
|
editor.appendChild(editorContainer);
|
||||||
|
}
|
||||||
|
editorContainer.classList.remove('hidden');
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div bind:this={editorContainer} class="hidden"><slot>Slot</slot></div>
|
||||||
|
<table style="--grid-template-columns: {gridTemplateColumns}">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
{#each columns as column}
|
||||||
|
<th bind:this={column.element}>{column.title} <span class="resize-handle" on:mousedown={initResize}></span></th>
|
||||||
|
{/each}
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{#each $rows as row (rowKey(row))}
|
||||||
|
<!-- data-key="{rowKey(row)}"-->
|
||||||
|
<tr class:hidden={row === $edited} on:mousedown={(e) => selectRow(e, row)} on:dblclick={(e) => editRow(e, row)}>
|
||||||
|
{#each columns as column}
|
||||||
|
<td>{column.value(row)}</td>
|
||||||
|
{/each}
|
||||||
|
<td class="editor"></td>
|
||||||
|
</tr>
|
||||||
|
{/each}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
<button on:click={() => {$edited = null}} type="button">Stop Editing</button>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
table {
|
||||||
|
width: auto;
|
||||||
|
-webkit-box-flex: 1;
|
||||||
|
flex: 1;
|
||||||
|
display: grid;
|
||||||
|
border-collapse: collapse;
|
||||||
|
grid-template-columns: var(--grid-template-columns);
|
||||||
|
}
|
||||||
|
thead, tbody, tr {
|
||||||
|
display: contents;
|
||||||
|
}
|
||||||
|
th, td {
|
||||||
|
padding: 15px;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
user-select: none;
|
||||||
|
}
|
||||||
|
th {
|
||||||
|
position: -webkit-sticky;
|
||||||
|
position: sticky;
|
||||||
|
top: 0;
|
||||||
|
background: #5cb85c;
|
||||||
|
text-align: left;
|
||||||
|
font-weight: normal;
|
||||||
|
font-size: 1.1rem;
|
||||||
|
color: white;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
th:last-child {
|
||||||
|
border: 0;
|
||||||
|
}
|
||||||
|
.resize-handle {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
background: black;
|
||||||
|
opacity: 0;
|
||||||
|
width: 3px;
|
||||||
|
cursor: col-resize;
|
||||||
|
}
|
||||||
|
th:last-child .resize-handle {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
.resize-handle:hover, .header--being-resized .resize-handle {
|
||||||
|
opacity: 0.5;
|
||||||
|
}
|
||||||
|
th:hover .resize-handle {
|
||||||
|
opacity: 0.3;
|
||||||
|
}
|
||||||
|
td {
|
||||||
|
padding-top: 10px;
|
||||||
|
padding-bottom: 10px;
|
||||||
|
color: #808080;
|
||||||
|
}
|
||||||
|
tr:nth-child(even) {
|
||||||
|
background: #f8f6ff;
|
||||||
|
}
|
||||||
|
:global(.selected), :global(.selected) > td {
|
||||||
|
background-color: yellow;
|
||||||
|
}
|
||||||
|
.editor {
|
||||||
|
grid-column: 1 / 4;
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
/*:global(td.hidden) {*/
|
||||||
|
/* display: none;*/
|
||||||
|
/*}*/
|
||||||
|
:global(tr.hidden) > td:not(.editor) {
|
||||||
|
display: none !important;
|
||||||
|
}
|
||||||
|
:global(tr.hidden) > td.editor {
|
||||||
|
display: block !important;
|
||||||
|
}
|
||||||
|
:global(div.hidden) {
|
||||||
|
display: none !important;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
50
imports/ui/ListUsers.svelte
Normal file
50
imports/ui/ListUsers.svelte
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
<script>
|
||||||
|
import {Meteor} from "meteor/meteor";
|
||||||
|
import TestUsers from "./TestUsers.svelte";
|
||||||
|
import {writable} from "svelte/store";
|
||||||
|
|
||||||
|
$: users = Meteor.users.find({});
|
||||||
|
|
||||||
|
|
||||||
|
const columns = [
|
||||||
|
{
|
||||||
|
key: "_id",
|
||||||
|
title: "ID",
|
||||||
|
value: v => v._id,
|
||||||
|
minWidth: 20,
|
||||||
|
weight: 1,
|
||||||
|
cls: "id",
|
||||||
|
}, {
|
||||||
|
key: "name",
|
||||||
|
title: "Name",
|
||||||
|
value: v => v.profile.name,
|
||||||
|
minWidth: 100,
|
||||||
|
weight: 1,
|
||||||
|
cls: "name",
|
||||||
|
}, {
|
||||||
|
key: "roles",
|
||||||
|
title: "Roles",
|
||||||
|
value: user => {
|
||||||
|
return Roles.getRolesForUser(user, {anyScope: true});
|
||||||
|
},
|
||||||
|
minWidth: 150,
|
||||||
|
weight: 2,
|
||||||
|
cls: "roles",
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
const getRowKey = user => {return user._id;}
|
||||||
|
let edited = writable(null);
|
||||||
|
</script>
|
||||||
|
|
||||||
|
{#await Meteor.subscribe('allUsers')}
|
||||||
|
Loading...
|
||||||
|
{:then allUsers}
|
||||||
|
<TestUsers bind:rows="{users}" columns="{columns}" rowKey="{getRowKey}" bind:edited={edited}>
|
||||||
|
{#if $edited}
|
||||||
|
<input type="text" bind:value={$edited.profile.name}/>
|
||||||
|
{/if}
|
||||||
|
</TestUsers>
|
||||||
|
{:catch error}
|
||||||
|
{error.message}
|
||||||
|
{/await}
|
||||||
158
imports/ui/NotAuthorized.svelte
Normal file
158
imports/ui/NotAuthorized.svelte
Normal file
@@ -0,0 +1,158 @@
|
|||||||
|
|
||||||
|
<style>
|
||||||
|
.maincontainer {
|
||||||
|
position: relative;
|
||||||
|
top: -50px;
|
||||||
|
transform: scale(0.8);
|
||||||
|
background: url("/public/images/forbidden/HauntedHouseBackground.png");
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
background-position: center;
|
||||||
|
background-size: 700px 600px;
|
||||||
|
width: 800px;
|
||||||
|
height: 600px;
|
||||||
|
margin: 0px auto;
|
||||||
|
display: grid;
|
||||||
|
}
|
||||||
|
.foregroundimg {
|
||||||
|
position: relative;
|
||||||
|
width: 100%;
|
||||||
|
top: -230px;
|
||||||
|
z-index: 5;
|
||||||
|
}
|
||||||
|
.errorcode {
|
||||||
|
position: relative;
|
||||||
|
top: -200px;
|
||||||
|
font-family: 'Creepster', cursive;
|
||||||
|
color: white;
|
||||||
|
text-align: center;
|
||||||
|
font-size: 6em;
|
||||||
|
letter-spacing: 0.1em;
|
||||||
|
}
|
||||||
|
.errortext {
|
||||||
|
position: relative;
|
||||||
|
top: -260px;
|
||||||
|
color: #FBD130;
|
||||||
|
text-align: center;
|
||||||
|
text-transform: uppercase;
|
||||||
|
font-size: 1.8em;
|
||||||
|
}
|
||||||
|
.bat {
|
||||||
|
opacity: 0;
|
||||||
|
position: relative;
|
||||||
|
transform-origin: center;
|
||||||
|
z-index: 3;
|
||||||
|
}
|
||||||
|
.bat:nth-child(1) {
|
||||||
|
top: 380px;
|
||||||
|
left: 120px;
|
||||||
|
transform: scale(0.5);
|
||||||
|
animation: 13s 1s flyBat1 infinite linear;
|
||||||
|
}
|
||||||
|
.bat:nth-child(2) {
|
||||||
|
top: 280px;
|
||||||
|
left: 80px;
|
||||||
|
transform: scale(0.3);
|
||||||
|
animation: 8s 4s flyBat2 infinite linear;
|
||||||
|
}
|
||||||
|
.bat:nth-child(3) {
|
||||||
|
top: 200px;
|
||||||
|
left: 150px;
|
||||||
|
transform: scale(0.4);
|
||||||
|
animation: 12s 2s flyBat3 infinite linear;
|
||||||
|
}
|
||||||
|
.body {
|
||||||
|
position: relative;
|
||||||
|
width: 50px;
|
||||||
|
top: 12px;
|
||||||
|
}
|
||||||
|
.wing {
|
||||||
|
width: 150px;
|
||||||
|
position: relative;
|
||||||
|
transform-origin: right center;
|
||||||
|
}
|
||||||
|
.leftwing {
|
||||||
|
left: 30px;
|
||||||
|
animation: 0.8s flapLeft infinite ease-in-out;
|
||||||
|
}
|
||||||
|
.rightwing {
|
||||||
|
left: -180px;
|
||||||
|
transform: scaleX(-1);
|
||||||
|
animation: 0.8s flapRight infinite ease-in-out;
|
||||||
|
}
|
||||||
|
@keyframes flapLeft {
|
||||||
|
0% { transform: rotateZ(0); }
|
||||||
|
50% { transform: rotateZ(10deg) rotateY(40deg); }
|
||||||
|
100% { transform: rotateZ(0); }
|
||||||
|
}
|
||||||
|
@keyframes flapRight {
|
||||||
|
0% { transform: scaleX(-1) rotateZ(0); }
|
||||||
|
50% { transform: scaleX(-1) rotateZ(10deg) rotateY(40deg); }
|
||||||
|
100% { transform: scaleX(-1) rotateZ(0); }
|
||||||
|
}
|
||||||
|
@keyframes flyBat1 {
|
||||||
|
0% { opacity: 1; transform: scale(0.5)}
|
||||||
|
25% { opacity: 1; transform: scale(0.5) translate(-400px, -330px) }
|
||||||
|
50% { opacity: 1; transform: scale(0.5) translate(400px, -800px) }
|
||||||
|
75% { opacity: 1; transform: scale(0.5) translate(600px, 100px) }
|
||||||
|
100% { opacity: 1; transform: scale(0.5) translate(100px, 300px) }
|
||||||
|
}
|
||||||
|
@keyframes flyBat2 {
|
||||||
|
0% { opacity: 1; transform: scale(0.3)}
|
||||||
|
25% { opacity: 1; transform: scale(0.3) translate(200px, -330px) }
|
||||||
|
50% { opacity: 1; transform: scale(0.3) translate(-300px, -800px) }
|
||||||
|
75% { opacity: 1; transform: scale(0.3) translate(-400px, 100px) }
|
||||||
|
100% { opacity: 1; transform: scale(0.3) translate(100px, 300px) }
|
||||||
|
}
|
||||||
|
@keyframes flyBat3 {
|
||||||
|
0% { opacity: 1; transform: scale(0.4)}
|
||||||
|
25% { opacity: 1; transform: scale(0.4) translate(-350px, -330px) }
|
||||||
|
50% { opacity: 1; transform: scale(0.4) translate(400px, -800px) }
|
||||||
|
75% { opacity: 1; transform: scale(0.4) translate(-600px, 100px) }
|
||||||
|
100% { opacity: 1; transform: scale(0.4) translate(100px, 300px) }
|
||||||
|
}
|
||||||
|
/*@media only screen and (max-width: 850px) {
|
||||||
|
.maincontainer {
|
||||||
|
transform: scale(0.6);
|
||||||
|
width: 600px;
|
||||||
|
height: 400px;
|
||||||
|
background-size: 600px 400px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.errortext {
|
||||||
|
font-size: 1em;
|
||||||
|
}
|
||||||
|
}*/
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<div className="container" style="background-color: #4d4242">
|
||||||
|
<div class="maincontainer row">
|
||||||
|
<div class="bat">
|
||||||
|
<img class="wing leftwing"
|
||||||
|
src="/images/forbidden/bat-wing.png">
|
||||||
|
<img class="body"
|
||||||
|
src="/images/forbidden/bat-body.png" alt="bat">
|
||||||
|
<img class="wing rightwing"
|
||||||
|
src="/images/forbidden/bat-wing.png">
|
||||||
|
</div>
|
||||||
|
<div class="bat">
|
||||||
|
<img class="wing leftwing"
|
||||||
|
src="/images/forbidden/bat-wing.png">
|
||||||
|
<img class="body"
|
||||||
|
src="/images/forbidden/bat-body.png" alt="bat">
|
||||||
|
<img class="wing rightwing"
|
||||||
|
src="/images/forbidden/bat-wing.png">
|
||||||
|
</div>
|
||||||
|
<div class="bat">
|
||||||
|
<img class="wing leftwing"
|
||||||
|
src="/images/forbidden/bat-wing.png">
|
||||||
|
<img class="body"
|
||||||
|
src="/images/forbidden/bat-body.png" alt="bat">
|
||||||
|
<img class="wing rightwing"
|
||||||
|
src="/images/forbidden/bat-wing.png">
|
||||||
|
</div>
|
||||||
|
<img class="foregroundimg" src="/images/forbidden/HauntedHouseForeground.png" alt="haunted house">
|
||||||
|
|
||||||
|
</div>
|
||||||
|
<h1 class="errorcode">ERROR 403</h1>
|
||||||
|
<div class="errortext">This area is forbidden. Turn back now!</div>
|
||||||
|
</div>
|
||||||
105
imports/ui/Table.svelte
Normal file
105
imports/ui/Table.svelte
Normal file
@@ -0,0 +1,105 @@
|
|||||||
|
<script>
|
||||||
|
import { createEventDispatcher } from "svelte";
|
||||||
|
import { onMount } from "svelte";
|
||||||
|
|
||||||
|
/** @type {Array<Object>} */
|
||||||
|
export let columns;
|
||||||
|
/** @type {Array<Object>} */
|
||||||
|
export let rows;
|
||||||
|
|
||||||
|
const dispatch = createEventDispatcher();
|
||||||
|
// Create a UUID for creating unique instance styles later.
|
||||||
|
const instanceId = Date.now().toString(36) + Math.random().toString(16).slice(2);
|
||||||
|
|
||||||
|
let columnByKey;
|
||||||
|
$: {
|
||||||
|
columnByKey = {};
|
||||||
|
columns.forEach(column => {
|
||||||
|
columnByKey[column.key] = column;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
let columnClasses = [];
|
||||||
|
// Create a custom class (style) for each column so we can control the column sizes.
|
||||||
|
$: {
|
||||||
|
// Remove old classes.
|
||||||
|
if(columnClasses && columnClasses.length) {
|
||||||
|
columnClasses.forEach(cls => {
|
||||||
|
try {
|
||||||
|
document.getElementsByTagName('head')[0].removeChild(cls);
|
||||||
|
}
|
||||||
|
catch(e) {console.log(e);}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create a unique class for each column so we can manage column sizes.
|
||||||
|
columns.forEach((column, index) => {
|
||||||
|
try {
|
||||||
|
let cls = document.createElement('style');
|
||||||
|
cls.type = 'text/css';
|
||||||
|
column.customClassName = 'svelte-' + instanceId + '-column-' + index;
|
||||||
|
cls.innerHTML = column.customClassName + "{min-width: " + column.width + "; max-width: " + column.width + "; width: " + column.width + ";}";
|
||||||
|
columnClasses[index] = cls;
|
||||||
|
document.getElementsByTagName('head')[0].appendChild(cls);
|
||||||
|
} catch(e) {console.log(e);}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Used to create a list of classes for tags.
|
||||||
|
const asStringArray = v => [].concat(v).filter(v => typeof v === "string" && v !== "").join(" ");
|
||||||
|
|
||||||
|
let section;
|
||||||
|
let table;
|
||||||
|
// onMount(async () => {
|
||||||
|
// //let hiddenHeaders = table.querySelectorAll("tbody tr:first-child td");
|
||||||
|
// let hiddenHeaders = table.querySelectorAll("th");
|
||||||
|
// section.querySelectorAll("th").forEach((th, index) => {
|
||||||
|
// hiddenHeaders[index].style.width = th.getBoundingClientRect().width + 'px';
|
||||||
|
// });
|
||||||
|
// });
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<section bind:this={section}>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
{#each columns as column}
|
||||||
|
<th class="{column.cls} cell" style="--width-{column.key}: {column.width}; min-width: var(--width-{column.key}); max-width: var(--width-{column.key}); width: var(--width-{column.key});">{column.title}</th>
|
||||||
|
{/each}
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
</section>
|
||||||
|
<table bind:this={table}>
|
||||||
|
<thead class="table-head">
|
||||||
|
<tr class="table-head">
|
||||||
|
{#each columns as column}
|
||||||
|
<th class="{column.cls} cell table-head" style="--width-{column.key}: {column.width}; min-width: var(--width-{column.key}); max-width: var(--width-{column.key}); width: var(--width-{column.key});" data-key="{column.key}">{column.title}</th>
|
||||||
|
{/each}
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{#each rows as row}
|
||||||
|
<tr>
|
||||||
|
{#each columns as column}
|
||||||
|
<td class="{column.cls} cell" style="--width-{column.key}: {column.width}; min-width: var(--width-{column.key}); max-width: var(--width-{column.key}); width: var(--width-{column.key});">{column.value(row)}</td>
|
||||||
|
{/each}
|
||||||
|
</tr>
|
||||||
|
{/each}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
section th:not(:last-child) {
|
||||||
|
border-right: 4px solid gray;
|
||||||
|
}
|
||||||
|
.table-head {
|
||||||
|
visibility: hidden;
|
||||||
|
line-height: 0;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
.cell {
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
335
imports/ui/Table2.svelte
Normal file
335
imports/ui/Table2.svelte
Normal file
@@ -0,0 +1,335 @@
|
|||||||
|
<script>
|
||||||
|
import { createEventDispatcher } from "svelte";
|
||||||
|
|
||||||
|
/** @type {Array<Object>} */
|
||||||
|
export let columns;
|
||||||
|
|
||||||
|
/** @type {Array<Object>} */
|
||||||
|
export let rows;
|
||||||
|
|
||||||
|
/** @type {Array<Object>} */
|
||||||
|
export let c_rows;
|
||||||
|
|
||||||
|
/** @type {Array<number>} */
|
||||||
|
export let sortOrders = [1, -1];
|
||||||
|
|
||||||
|
// READ AND WRITE
|
||||||
|
|
||||||
|
/** @type {string} */
|
||||||
|
export let sortBy = "";
|
||||||
|
|
||||||
|
/** @type {number} */
|
||||||
|
export let sortOrder = sortOrders?.[0] || 1;
|
||||||
|
|
||||||
|
/** @type {Object} */
|
||||||
|
export let filterSelections = {};
|
||||||
|
|
||||||
|
// expand
|
||||||
|
/** @type {Array.<string|number>} */
|
||||||
|
export let expanded = [];
|
||||||
|
|
||||||
|
// READ ONLY
|
||||||
|
|
||||||
|
/** @type {string} */
|
||||||
|
export let expandRowKey = null;
|
||||||
|
|
||||||
|
/** @type {string} */
|
||||||
|
export let expandSingle = false;
|
||||||
|
|
||||||
|
/** @type {string} */
|
||||||
|
export let iconAsc = "▲";
|
||||||
|
|
||||||
|
/** @type {string} */
|
||||||
|
export let iconDesc = "▼";
|
||||||
|
|
||||||
|
/** @type {string} */
|
||||||
|
export let iconSortable = "";
|
||||||
|
|
||||||
|
/** @type {string} */
|
||||||
|
export let iconExpand = "▼";
|
||||||
|
|
||||||
|
/** @type {string} */
|
||||||
|
export let iconExpanded = "▲";
|
||||||
|
|
||||||
|
/** @type {boolean} */
|
||||||
|
export let showExpandIcon = false;
|
||||||
|
|
||||||
|
/** @type {string} */
|
||||||
|
export let classNameTable = "";
|
||||||
|
|
||||||
|
/** @type {string} */
|
||||||
|
export let classNameThead = "";
|
||||||
|
|
||||||
|
/** @type {string} */
|
||||||
|
export let classNameTbody = "";
|
||||||
|
|
||||||
|
/** @type {string} */
|
||||||
|
export let classNameSelect = "";
|
||||||
|
|
||||||
|
/** @type {string} */
|
||||||
|
export let classNameInput = "";
|
||||||
|
|
||||||
|
/** @type {string} */
|
||||||
|
export let classNameRow = "";
|
||||||
|
|
||||||
|
/** @type {string} */
|
||||||
|
export let classNameCell = "";
|
||||||
|
|
||||||
|
/** @type {string} class added to the expanded row*/
|
||||||
|
export let classNameRowExpanded = "";
|
||||||
|
|
||||||
|
/** @type {string} class added to the expanded row*/
|
||||||
|
export let classNameExpandedContent = "";
|
||||||
|
|
||||||
|
/** @type {string} class added to the cell that allows expanding/closing */
|
||||||
|
export let classNameCellExpand = "";
|
||||||
|
|
||||||
|
const dispatch = createEventDispatcher();
|
||||||
|
|
||||||
|
let sortFunction = () => "";
|
||||||
|
|
||||||
|
// Validation
|
||||||
|
if (!Array.isArray(expanded)) throw "'expanded' needs to be an array";
|
||||||
|
|
||||||
|
let showFilterHeader = columns.some(c => {
|
||||||
|
// check if there are any filter or search headers
|
||||||
|
return c.filterOptions !== undefined || c.searchValue !== undefined;
|
||||||
|
});
|
||||||
|
let filterValues = {};
|
||||||
|
let columnByKey;
|
||||||
|
$: {
|
||||||
|
columnByKey = {};
|
||||||
|
columns.forEach(col => {
|
||||||
|
columnByKey[col.key] = col;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
$: colspan = (showExpandIcon ? 1 : 0) + columns.length;
|
||||||
|
|
||||||
|
console.log(rows);
|
||||||
|
|
||||||
|
$: c_rows = rows
|
||||||
|
.filter(r => {
|
||||||
|
// get search and filter results/matches
|
||||||
|
return Object.keys(filterSelections).every(f => {
|
||||||
|
// check search (text input) matches
|
||||||
|
let resSearch =
|
||||||
|
filterSelections[f] === "" ||
|
||||||
|
(columnByKey[f].searchValue &&
|
||||||
|
(columnByKey[f].searchValue(r) + "")
|
||||||
|
.toLocaleLowerCase()
|
||||||
|
.indexOf((filterSelections[f] + "").toLocaleLowerCase()) >= 0);
|
||||||
|
|
||||||
|
// check filter (dropdown) matches
|
||||||
|
let resFilter =
|
||||||
|
resSearch ||
|
||||||
|
filterSelections[f] === undefined ||
|
||||||
|
// default to value() if filterValue() not provided in col
|
||||||
|
filterSelections[f] ===
|
||||||
|
(typeof columnByKey[f].filterValue === "function"
|
||||||
|
? columnByKey[f].filterValue(r)
|
||||||
|
: columnByKey[f].value(r));
|
||||||
|
return resFilter;
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.map(r =>
|
||||||
|
Object.assign({}, r, {
|
||||||
|
// internal row property for sort order
|
||||||
|
$sortOn: sortFunction(r),
|
||||||
|
// internal row property for expanded rows
|
||||||
|
$expanded:
|
||||||
|
expandRowKey !== null && expanded.indexOf(r[expandRowKey]) >= 0,
|
||||||
|
})
|
||||||
|
)
|
||||||
|
.sort((a, b) => {
|
||||||
|
if (!sortBy) return 0;
|
||||||
|
else if (a.$sortOn > b.$sortOn) return sortOrder;
|
||||||
|
else if (a.$sortOn < b.$sortOn) return -sortOrder;
|
||||||
|
return 0;
|
||||||
|
});
|
||||||
|
|
||||||
|
const asStringArray = v =>
|
||||||
|
[]
|
||||||
|
.concat(v)
|
||||||
|
.filter(v => typeof v === "string" && v !== "")
|
||||||
|
.join(" ");
|
||||||
|
|
||||||
|
const calculateFilterValues = () => {
|
||||||
|
filterValues = {};
|
||||||
|
columns.forEach(c => {
|
||||||
|
if (typeof c.filterOptions === "function") {
|
||||||
|
filterValues[c.key] = c.filterOptions(rows);
|
||||||
|
} else if (Array.isArray(c.filterOptions)) {
|
||||||
|
// if array of strings is provided, use it for name and value
|
||||||
|
filterValues[c.key] = c.filterOptions.map(val => ({
|
||||||
|
name: val,
|
||||||
|
value: val,
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
$: {
|
||||||
|
let col = columnByKey[sortBy];
|
||||||
|
if (
|
||||||
|
col !== undefined &&
|
||||||
|
col.sortable === true &&
|
||||||
|
typeof col.value === "function"
|
||||||
|
) {
|
||||||
|
sortFunction = r => col.value(r);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$: {
|
||||||
|
// if filters are enabled, watch rows and columns
|
||||||
|
if (showFilterHeader && columns && rows) {
|
||||||
|
calculateFilterValues();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const updateSortOrder = colKey => {
|
||||||
|
return colKey === sortBy
|
||||||
|
? sortOrders[
|
||||||
|
(sortOrders.findIndex(o => o === sortOrder) + 1) % sortOrders.length
|
||||||
|
]
|
||||||
|
: sortOrders[0];
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleClickCol = (event, col) => {
|
||||||
|
if (col.sortable) {
|
||||||
|
sortOrder = updateSortOrder(col.key);
|
||||||
|
sortBy = sortOrder ? col.key : undefined;
|
||||||
|
}
|
||||||
|
dispatch("clickCol", { event, col, key: col.key });
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleClickRow = (event, row) => {
|
||||||
|
dispatch("clickRow", { event, row });
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleClickExpand = (event, row) => {
|
||||||
|
row.$expanded = !row.$expanded;
|
||||||
|
const keyVal = row[expandRowKey];
|
||||||
|
if (expandSingle && row.$expanded) {
|
||||||
|
expanded = [keyVal];
|
||||||
|
} else if (expandSingle) {
|
||||||
|
expanded = [];
|
||||||
|
} else if (!row.$expanded) {
|
||||||
|
expanded = expanded.filter(r => r != keyVal);
|
||||||
|
} else {
|
||||||
|
expanded = [...expanded, keyVal];
|
||||||
|
}
|
||||||
|
dispatch("clickExpand", { event, row });
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleClickCell = (event, row, key) => {
|
||||||
|
dispatch("clickCell", { event, row, key });
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<table class={asStringArray(classNameTable)}>
|
||||||
|
<thead class={asStringArray(classNameThead)}>
|
||||||
|
{#if showFilterHeader}
|
||||||
|
<tr>
|
||||||
|
{#each columns as col}
|
||||||
|
<th class={asStringArray([col.headerFilterClass])}>
|
||||||
|
{#if col.searchValue !== undefined}
|
||||||
|
<input
|
||||||
|
bind:value={filterSelections[col.key]}
|
||||||
|
class={asStringArray(classNameInput)}
|
||||||
|
/>
|
||||||
|
{:else if filterValues[col.key] !== undefined}
|
||||||
|
<select
|
||||||
|
bind:value={filterSelections[col.key]}
|
||||||
|
class={asStringArray(classNameSelect)}
|
||||||
|
>
|
||||||
|
<option value={undefined} />
|
||||||
|
{#each filterValues[col.key] as option}
|
||||||
|
<option value={option.value}>{option.name}</option>
|
||||||
|
{/each}
|
||||||
|
</select>
|
||||||
|
{/if}
|
||||||
|
</th>
|
||||||
|
{/each}
|
||||||
|
{#if showExpandIcon}
|
||||||
|
<th />
|
||||||
|
{/if}
|
||||||
|
</tr>
|
||||||
|
{/if}
|
||||||
|
<slot name="header" {sortOrder} {sortBy}>
|
||||||
|
<tr>
|
||||||
|
{#each columns as col}
|
||||||
|
<th
|
||||||
|
on:click={e => handleClickCol(e, col)}
|
||||||
|
class={asStringArray([
|
||||||
|
col.sortable ? "isSortable" : "",
|
||||||
|
col.headerClass,
|
||||||
|
])}
|
||||||
|
>
|
||||||
|
{col.title}
|
||||||
|
{#if sortBy === col.key}
|
||||||
|
{@html sortOrder === 1 ? iconAsc : iconDesc}
|
||||||
|
{:else if col.sortable}
|
||||||
|
{@html iconSortable}
|
||||||
|
{/if}
|
||||||
|
</th>
|
||||||
|
{/each}
|
||||||
|
{#if showExpandIcon}
|
||||||
|
<th />
|
||||||
|
{/if}
|
||||||
|
</tr>
|
||||||
|
</slot>
|
||||||
|
</thead>
|
||||||
|
|
||||||
|
<tbody class={asStringArray(classNameTbody)}>
|
||||||
|
{#each c_rows as row, n}
|
||||||
|
<slot name="row" {row} {n}>
|
||||||
|
<tr on:click={e => { handleClickRow(e, row); }} class={asStringArray([classNameRow, row.$expanded && classNameRowExpanded])}>
|
||||||
|
{#each columns as col}
|
||||||
|
<td on:click={e => {handleClickCell(e, row, col.key);}} class={asStringArray([col.class, classNameCell])}>
|
||||||
|
{#if col.renderComponent}
|
||||||
|
<svelte:component
|
||||||
|
this={col.renderComponent.component || col.renderComponent}
|
||||||
|
{...col.renderComponent.props || {}}
|
||||||
|
{row}
|
||||||
|
{col}
|
||||||
|
/>
|
||||||
|
{:else}
|
||||||
|
{@html col.renderValue ? col.renderValue(row) : col.value(row)}
|
||||||
|
{/if}
|
||||||
|
</td>
|
||||||
|
{/each}
|
||||||
|
{#if showExpandIcon}
|
||||||
|
<td on:click={e => handleClickExpand(e, row)} class={asStringArray(["isClickable", classNameCellExpand])}>
|
||||||
|
{@html row.$expanded ? iconExpand : iconExpanded}
|
||||||
|
</td>
|
||||||
|
{/if}
|
||||||
|
</tr>
|
||||||
|
{#if row.$expanded}
|
||||||
|
<tr class={asStringArray(classNameExpandedContent)}>
|
||||||
|
<td {colspan}>
|
||||||
|
<slot name="expanded" {row} {n} />
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
{/if}
|
||||||
|
</slot>
|
||||||
|
{/each}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
table {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
.isSortable {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.isClickable {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
tr th select {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
66
imports/ui/TestTable.svelte
Normal file
66
imports/ui/TestTable.svelte
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
|
||||||
|
<script>
|
||||||
|
import {Route, router, meta} from 'tinro';
|
||||||
|
import {Meteor} from "meteor/meteor";
|
||||||
|
import FlexTable from "./FlexTable.svelte";
|
||||||
|
import {useTracker} from "meteor/rdb:svelte-meteor-data";
|
||||||
|
import {writable} from "svelte/store";
|
||||||
|
|
||||||
|
const columns = [
|
||||||
|
{
|
||||||
|
key: "_id",
|
||||||
|
title: "ID",
|
||||||
|
value: v => v._id,
|
||||||
|
minWidth: 20,
|
||||||
|
weight: 1,
|
||||||
|
}, {
|
||||||
|
key: "text",
|
||||||
|
title: "Text",
|
||||||
|
value: v => v.text,
|
||||||
|
minWidth: 100,
|
||||||
|
weight: 1,
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
let rows = writable([
|
||||||
|
{
|
||||||
|
_id: "1",
|
||||||
|
text: "A"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
_id: "2",
|
||||||
|
text: "B"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
_id: "3",
|
||||||
|
text: "C"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
_id: "4",
|
||||||
|
text: "D"
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
|
||||||
|
const getRowKey = (row) => row._id;
|
||||||
|
let edited = writable(null);
|
||||||
|
let text = "";
|
||||||
|
const addRow = () => {
|
||||||
|
$rows[$rows.length] = {_id: "" + ($rows.length + 1), text};
|
||||||
|
text = "";
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
<Route path="/">
|
||||||
|
<div class="container">
|
||||||
|
<div class="row col-12 table">
|
||||||
|
<FlexTable columns="{columns}" bind:rows="{$rows}" rowKey="{getRowKey}" edited="{edited}">
|
||||||
|
My Editor....
|
||||||
|
</FlexTable>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="container">
|
||||||
|
<div class="row col-12">
|
||||||
|
<input type="text" bind:value={text}/>
|
||||||
|
<button type="button" on:click={addRow}>Add</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Route>
|
||||||
181
imports/ui/TestUsers.svelte
Normal file
181
imports/ui/TestUsers.svelte
Normal file
@@ -0,0 +1,181 @@
|
|||||||
|
<script>
|
||||||
|
export let rows;
|
||||||
|
export let columns;
|
||||||
|
export let rowKey;
|
||||||
|
export let edited;
|
||||||
|
|
||||||
|
// Setup a width for each column.
|
||||||
|
columns.forEach(column => {
|
||||||
|
let min = column.minWidth ? Math.max(10, column.minWidth) : 10;
|
||||||
|
let weight = column.weight ? Math.max(1, column.weight) : 1;
|
||||||
|
column.width = 'minmax(' + min + 'px, ' + weight + 'fr)';
|
||||||
|
});
|
||||||
|
let gridTemplateColumns = columns.map(({width}) => width).join(' ');
|
||||||
|
|
||||||
|
let headerBeingResized = null;
|
||||||
|
let horizontalScrollOffset = 0;
|
||||||
|
const initResize = ({target}) => {
|
||||||
|
headerBeingResized = target.parentNode;
|
||||||
|
window.addEventListener('mousemove', onMouseMove);
|
||||||
|
window.addEventListener('mouseup', completeResize);
|
||||||
|
headerBeingResized.classList.add('header--being-resized');
|
||||||
|
};
|
||||||
|
const completeResize = () => {
|
||||||
|
window.removeEventListener('mousemove', onMouseMove);
|
||||||
|
window.removeEventListener('mouseup', completeResize);
|
||||||
|
headerBeingResized.classList.remove('header--being-resized');
|
||||||
|
headerBeingResized = null;
|
||||||
|
};
|
||||||
|
const onMouseMove = e => {
|
||||||
|
try {
|
||||||
|
// Calculate the desired width.
|
||||||
|
horizontalScrollOffset = document.documentElement.scrollLeft;
|
||||||
|
let parentX = Math.round(headerBeingResized.getBoundingClientRect().x);
|
||||||
|
const width = horizontalScrollOffset + (e.clientX - parentX);
|
||||||
|
// Update the column object with the new size value.
|
||||||
|
const column = columns.find(({element}) => element === headerBeingResized);
|
||||||
|
column.width = Math.max(column.minWidth, width) + "px";
|
||||||
|
// Ensure all the column widths are converted to fixed sizes.
|
||||||
|
columns.forEach((column, index) => {
|
||||||
|
if((index < columns.length - 1) && (column.width.startsWith('minmax'))) {
|
||||||
|
column.width = parseInt(column.element.clientWidth, 10) + 'px';
|
||||||
|
}
|
||||||
|
});
|
||||||
|
// Render the new column sizes.
|
||||||
|
gridTemplateColumns = columns.map(({width}) => width).join(' ');
|
||||||
|
} catch(e) {console.log(e);}
|
||||||
|
}
|
||||||
|
|
||||||
|
let selectedRowElement = null;
|
||||||
|
const selectRow = (e, row) => {
|
||||||
|
let element = e.target;
|
||||||
|
|
||||||
|
while(element && element.nodeName !== "TR") element = element.parentNode;
|
||||||
|
|
||||||
|
if(selectedRowElement) {
|
||||||
|
selectedRowElement.classList.remove('selected');
|
||||||
|
}
|
||||||
|
|
||||||
|
selectedRowElement = element;
|
||||||
|
element.classList.add('selected');
|
||||||
|
}
|
||||||
|
|
||||||
|
let editorContainer;
|
||||||
|
const editRow = (e, row) => {
|
||||||
|
let element = e.target;
|
||||||
|
while(element && element.nodeName !== "TR") element = element.parentNode;
|
||||||
|
let editor = element.querySelector('.editor');
|
||||||
|
|
||||||
|
// Save the edited row so the editor has access to it.
|
||||||
|
$edited = row;
|
||||||
|
|
||||||
|
if(editor) {
|
||||||
|
editor.appendChild(editorContainer);
|
||||||
|
}
|
||||||
|
editorContainer.classList.remove('hidden');
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div bind:this={editorContainer} class="hidden"><slot>Slot</slot></div>
|
||||||
|
<table style="--grid-template-columns: {gridTemplateColumns}">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
{#each columns as column}
|
||||||
|
<th bind:this={column.element}>{column.title} <span class="resize-handle" on:mousedown={initResize}></span></th>
|
||||||
|
{/each}
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{#each $rows as row (rowKey(row))}
|
||||||
|
<!-- data-key="{rowKey(row)}"-->
|
||||||
|
<tr class:hidden={row === $edited} on:mousedown={(e) => selectRow(e, row)} on:dblclick={(e) => editRow(e, row)}>
|
||||||
|
{#each columns as column}
|
||||||
|
<td>{column.value(row)}</td>
|
||||||
|
{/each}
|
||||||
|
<td class="editor"></td>
|
||||||
|
</tr>
|
||||||
|
{/each}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
<button on:click={() => {$edited = null}} type="button">Stop Editing</button>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
table {
|
||||||
|
width: auto;
|
||||||
|
-webkit-box-flex: 1;
|
||||||
|
flex: 1;
|
||||||
|
display: grid;
|
||||||
|
border-collapse: collapse;
|
||||||
|
grid-template-columns: var(--grid-template-columns);
|
||||||
|
}
|
||||||
|
thead, tbody, tr {
|
||||||
|
display: contents;
|
||||||
|
}
|
||||||
|
th, td {
|
||||||
|
padding: 15px;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
user-select: none;
|
||||||
|
}
|
||||||
|
th {
|
||||||
|
position: -webkit-sticky;
|
||||||
|
position: sticky;
|
||||||
|
top: 0;
|
||||||
|
background: #5cb85c;
|
||||||
|
text-align: left;
|
||||||
|
font-weight: normal;
|
||||||
|
font-size: 1.1rem;
|
||||||
|
color: white;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
th:last-child {
|
||||||
|
border: 0;
|
||||||
|
}
|
||||||
|
.resize-handle {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
background: black;
|
||||||
|
opacity: 0;
|
||||||
|
width: 3px;
|
||||||
|
cursor: col-resize;
|
||||||
|
}
|
||||||
|
th:last-child .resize-handle {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
.resize-handle:hover, .header--being-resized .resize-handle {
|
||||||
|
opacity: 0.5;
|
||||||
|
}
|
||||||
|
th:hover .resize-handle {
|
||||||
|
opacity: 0.3;
|
||||||
|
}
|
||||||
|
td {
|
||||||
|
padding-top: 10px;
|
||||||
|
padding-bottom: 10px;
|
||||||
|
color: #808080;
|
||||||
|
}
|
||||||
|
tr:nth-child(even) {
|
||||||
|
background: #f8f6ff;
|
||||||
|
}
|
||||||
|
:global(.selected), :global(.selected) > td {
|
||||||
|
background-color: yellow;
|
||||||
|
}
|
||||||
|
.editor {
|
||||||
|
grid-column: 1 / 4;
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
/*:global(td.hidden) {*/
|
||||||
|
/* display: none;*/
|
||||||
|
/*}*/
|
||||||
|
:global(tr.hidden) > td:not(.editor) {
|
||||||
|
display: none !important;
|
||||||
|
}
|
||||||
|
:global(tr.hidden) > td.editor {
|
||||||
|
display: block !important;
|
||||||
|
}
|
||||||
|
:global(div.hidden) {
|
||||||
|
display: none !important;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
191
imports/ui/Users.svelte
Normal file
191
imports/ui/Users.svelte
Normal file
@@ -0,0 +1,191 @@
|
|||||||
|
|
||||||
|
<script>
|
||||||
|
import {Route, router, meta} from 'tinro';
|
||||||
|
import {Meteor} from "meteor/meteor";
|
||||||
|
import FlexTable from "./FlexTable.svelte";
|
||||||
|
import {useTracker} from "meteor/rdb:svelte-meteor-data";
|
||||||
|
import {writable} from "svelte/store";
|
||||||
|
|
||||||
|
const columns = [
|
||||||
|
{
|
||||||
|
key: "_id",
|
||||||
|
title: "ID",
|
||||||
|
value: v => v._id,
|
||||||
|
minWidth: 20,
|
||||||
|
weight: 1,
|
||||||
|
cls: "id",
|
||||||
|
}, {
|
||||||
|
key: "name",
|
||||||
|
title: "Name",
|
||||||
|
value: v => v.profile.name,
|
||||||
|
minWidth: 100,
|
||||||
|
weight: 1,
|
||||||
|
cls: "name",
|
||||||
|
}, {
|
||||||
|
key: "roles",
|
||||||
|
title: "Roles",
|
||||||
|
value: user => {
|
||||||
|
return Roles.getRolesForUser(user, {anyScope: true});
|
||||||
|
},
|
||||||
|
minWidth: 150,
|
||||||
|
weight: 2,
|
||||||
|
cls: "roles",
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
const getRowKey = user => {return user._id;}
|
||||||
|
|
||||||
|
function changeColWidth() {
|
||||||
|
columns[0].width = '200px';
|
||||||
|
}
|
||||||
|
|
||||||
|
const editRow = (row) => {
|
||||||
|
//TODO: Setup the editor for the given row.
|
||||||
|
}
|
||||||
|
let edited = writable(null);
|
||||||
|
let editedPermissions = null;
|
||||||
|
|
||||||
|
$: rows = Meteor.users.find({});
|
||||||
|
|
||||||
|
edited.subscribe((value) => {
|
||||||
|
if(value) {
|
||||||
|
editedPermissions = {
|
||||||
|
isAdmin: Roles.userIsInRole(value, "admin", {anyScope: true}),
|
||||||
|
laptopManagement: Roles.userIsInRole(value, "laptop-management", {anyScope: true}),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
const applyChanges = () => {
|
||||||
|
let roles = [];
|
||||||
|
|
||||||
|
if(editedPermissions.isAdmin) {
|
||||||
|
roles.push('admin');
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if(editedPermissions.laptopManagement) {
|
||||||
|
roles.push('laptop-management');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Meteor.call("users.setUserRoles", $edited._id, roles);
|
||||||
|
edited.set(null);
|
||||||
|
}
|
||||||
|
const rejectChanges = () => {
|
||||||
|
edited.set(null);
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<Route path="/" let:meta>
|
||||||
|
<div class="container">
|
||||||
|
<div class="row col-12 table">
|
||||||
|
{#await Promise.all([Meteor.subscribe('allUsers'), Meteor.subscribe('allRoleAssignments')])}
|
||||||
|
Loading...
|
||||||
|
{:then allUsers}
|
||||||
|
<FlexTable bind:rows={rows} columns="{columns}" rowKey="{getRowKey}" bind:edited="{edited}">
|
||||||
|
{#if editedPermissions}
|
||||||
|
<div class="editorContainer">
|
||||||
|
<label style="grid-column: 1/4; font-weight: 800; border-bottom: 2px solid #888; margin-bottom: 0.5rem">{$edited.profile.name}</label>
|
||||||
|
<label class="checkbox" style="grid-column: 1/4;"><input type="checkbox" bind:checked="{editedPermissions.isAdmin}" style="--form-control-color: black"/> Administrator</label>
|
||||||
|
<div class="insetPermissions">
|
||||||
|
<label class="checkbox"><input type="checkbox" disabled="{editedPermissions.isAdmin}" bind:checked="{editedPermissions.laptopManagement}" style="--form-control-color: black"/> Laptop Management</label>
|
||||||
|
</div>
|
||||||
|
<button type="button" style="grid-column: 2/2;" class="button accept-button" on:click={applyChanges}> </button>
|
||||||
|
<button type="button" style="grid-column: 3/3;" class="button reject-button" on:click={rejectChanges}> </button>
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
</FlexTable>
|
||||||
|
<button type="button" on:click="{changeColWidth}">Change Width</button>
|
||||||
|
{:catch error}
|
||||||
|
{error.message}
|
||||||
|
{/await}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Route>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
:root {
|
||||||
|
--form-control-disabled: #959495;
|
||||||
|
}
|
||||||
|
.checkbox {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 1em auto;
|
||||||
|
gap: 0.5em;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
input[type="checkbox"] {
|
||||||
|
/* Add if not using autoprefixer */
|
||||||
|
-webkit-appearance: none;
|
||||||
|
appearance: none;
|
||||||
|
/* For iOS < 15 to remove gradient background */
|
||||||
|
background-color: #fff;
|
||||||
|
/* Not removed via appearance */
|
||||||
|
margin: 0;
|
||||||
|
font: inherit;
|
||||||
|
color: currentColor;
|
||||||
|
width: 1.15em;
|
||||||
|
height: 1.15em;
|
||||||
|
border: 0.15em solid currentColor;
|
||||||
|
border-radius: 0.15em;
|
||||||
|
/*transform: translateY(-0.075em);*/
|
||||||
|
display: grid;
|
||||||
|
place-content: center;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
input[type="checkbox"]::before {
|
||||||
|
content: "";
|
||||||
|
width: 0.65em;
|
||||||
|
height: 0.65em;
|
||||||
|
transform: scale(0);
|
||||||
|
transition: 120ms transform ease-in-out;
|
||||||
|
box-shadow: inset 1em 1em var(--form-control-color);
|
||||||
|
/* Windows High Contrast Mode */
|
||||||
|
background-color: CanvasText;
|
||||||
|
/* Make it a check mark shape. */
|
||||||
|
transform-origin: bottom left;
|
||||||
|
clip-path: polygon(14% 44%, 0 65%, 50% 100%, 100% 16%, 80% 0%, 43% 62%);
|
||||||
|
}
|
||||||
|
input[type="checkbox"]:checked::before {
|
||||||
|
transform: scale(1);
|
||||||
|
}
|
||||||
|
input[type="checkbox"]:focus {
|
||||||
|
outline: max(2px, 0.15em) solid currentColor;
|
||||||
|
outline-offset: max(2px, 0.15em);
|
||||||
|
}
|
||||||
|
input[type="checkbox"]:disabled {
|
||||||
|
--form-control-color: var(--form-control-disabled);
|
||||||
|
|
||||||
|
color: var(--form-control-disabled);
|
||||||
|
cursor: not-allowed;
|
||||||
|
}
|
||||||
|
.editorContainer {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: auto 2em 2em;
|
||||||
|
}
|
||||||
|
.insetPermissions {
|
||||||
|
margin-top: 0.5rem;
|
||||||
|
margin-left: 1.5rem;
|
||||||
|
display: grid;
|
||||||
|
grid-column: 1/4;
|
||||||
|
}
|
||||||
|
button.button {
|
||||||
|
border: none;
|
||||||
|
padding: 6px;
|
||||||
|
margin: 0;
|
||||||
|
cursor: pointer;
|
||||||
|
-webkit-appearance: button;
|
||||||
|
}
|
||||||
|
button.accept-button {
|
||||||
|
width: 2rem;
|
||||||
|
height: 2rem;
|
||||||
|
background-color: black;
|
||||||
|
clip-path: polygon(11% 69%, 31% 93%, 94% 8%, 73% 6%, 33% 62%, 12% 40%);
|
||||||
|
}
|
||||||
|
button.reject-button {
|
||||||
|
width: 2rem;
|
||||||
|
height: 2rem;
|
||||||
|
background-color: black;
|
||||||
|
clip-path: polygon(18% 90%, 37% 89%, 50% 64%, 62% 89%, 81% 88%, 59% 44%, 82% 13%, 62% 12%, 49% 35%, 28% 12%, 11% 12%, 37% 43%);
|
||||||
|
}
|
||||||
|
</style>
|
||||||
109
imports/ui/temp.svelte
Normal file
109
imports/ui/temp.svelte
Normal file
@@ -0,0 +1,109 @@
|
|||||||
|
|
||||||
|
<script>
|
||||||
|
import {Meteor} from "meteor/meteor";
|
||||||
|
import {Route, router} from 'tinro';
|
||||||
|
import {useTracker} from 'meteor/rdb:svelte-meteor-data';
|
||||||
|
import {Roles} from 'meteor/alanning:roles';
|
||||||
|
import Chromebooks from './Chromebooks.svelte';
|
||||||
|
import Users from './Users.svelte';
|
||||||
|
import ListUsers from './ListUsers.svelte';
|
||||||
|
import Admin from './Admin.svelte';
|
||||||
|
import Announcer from './Announcer.svelte';
|
||||||
|
|
||||||
|
// When the URL changes, run the code... in this case to scroll to the top.
|
||||||
|
router.subscribe(_ => window.scrollTo(0, 0));
|
||||||
|
|
||||||
|
$: currentUser = useTracker(() => Meteor.user());
|
||||||
|
$: canManageLaptops = false;
|
||||||
|
$: isAdmin = false;
|
||||||
|
|
||||||
|
Tracker.autorun(() => {
|
||||||
|
// For some reason currentUser is always null here, and is not reactive (user changes and this does not get re-called).
|
||||||
|
let user = Meteor.user();
|
||||||
|
canManageLaptops = user && Roles.userIsInRole(user._id, 'laptop-management', 'global');
|
||||||
|
isAdmin = user && Roles.userIsInRole(user._id, 'admin', 'global');
|
||||||
|
});
|
||||||
|
function performLogin() {
|
||||||
|
//Login style can be "popup" or "redirect". I am not sure we need to request and offline token.
|
||||||
|
Meteor.loginWithGoogle({loginStyle: "popup", requestOfflineToken: true}, (err) => {
|
||||||
|
if (err) {
|
||||||
|
console.log(err);
|
||||||
|
} else {
|
||||||
|
//console.log("Logged in");
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
function performLogout() {
|
||||||
|
Meteor.logout();
|
||||||
|
}
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<Announcer/>
|
||||||
|
|
||||||
|
<div class="container">
|
||||||
|
<header class="row">
|
||||||
|
<div class="col-12 logoContainer">
|
||||||
|
<img class="logo" src="/images/logo.svg"/>
|
||||||
|
<div class="login">
|
||||||
|
{#if !$currentUser}
|
||||||
|
<button type="button" role="button" on:click={performLogin}>Login</button>
|
||||||
|
{:else}
|
||||||
|
<button type="button" role="button" on:click={performLogout}>Logout</button>
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-12 center" style="margin-bottom: 0"><h1 style="margin-bottom: 0">District Central</h1></div>
|
||||||
|
<div class="col-12 center">
|
||||||
|
<div class="nav-separator"></div>
|
||||||
|
</div>
|
||||||
|
<nav class="col-12 center">
|
||||||
|
<a href="/">Home</a>
|
||||||
|
{#if canManageLaptops}
|
||||||
|
<a href="/chromebooks">Chromebooks</a>
|
||||||
|
{/if}
|
||||||
|
{#if canManageLaptops}
|
||||||
|
<a href="/users">Users</a>
|
||||||
|
{/if}
|
||||||
|
{#if isAdmin}
|
||||||
|
<a href="/admin">Admin</a>
|
||||||
|
{/if}
|
||||||
|
<!-- <a href="/TestTable">Test</a>-->
|
||||||
|
<!-- <a href="/ListUsers">List Users</a>-->
|
||||||
|
</nav>
|
||||||
|
</header>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<Route path="/">
|
||||||
|
<div class="container">
|
||||||
|
<div class="row">
|
||||||
|
TODO: Some statistics and such.
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Route>
|
||||||
|
<Route path="/ListUsers">
|
||||||
|
<ListUsers/>
|
||||||
|
</Route>
|
||||||
|
<Route path="/admin">
|
||||||
|
{#if isAdmin}
|
||||||
|
<Admin/>
|
||||||
|
{/if}
|
||||||
|
</Route>
|
||||||
|
<Route path="/chromebooks/*">
|
||||||
|
{#if canManageLaptops}
|
||||||
|
<Chromebooks/>
|
||||||
|
{:else}
|
||||||
|
<!-- User not authorized to use this UI. Don't render anything because it is likely the user is still loading and will have access in a moment. -->
|
||||||
|
{/if}
|
||||||
|
</Route>
|
||||||
|
<Route path="/users/*">
|
||||||
|
{#if isAdmin}
|
||||||
|
<Users/>
|
||||||
|
{:else}
|
||||||
|
<!-- User not authorized to use this UI. Don't render anything because it is likely the user is still loading and will have access in a moment. -->
|
||||||
|
{/if}
|
||||||
|
</Route>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
...
|
||||||
|
</style>
|
||||||
528
package-lock.json
generated
528
package-lock.json
generated
@@ -11,9 +11,13 @@
|
|||||||
"html5-qrcode": "^2.2.0",
|
"html5-qrcode": "^2.2.0",
|
||||||
"jquery": "^3.6.0",
|
"jquery": "^3.6.0",
|
||||||
"meteor-node-stubs": "^1.0.0",
|
"meteor-node-stubs": "^1.0.0",
|
||||||
|
"moment": "^2.29.2",
|
||||||
"mongodb": "^4.4.1",
|
"mongodb": "^4.4.1",
|
||||||
"svelte": "^3.46.4",
|
"svelte": "^3.46.4",
|
||||||
"tinro": "^0.6.12",
|
"tinro": "^0.6.12",
|
||||||
|
"underscore": "^1.13.2",
|
||||||
|
"winston": "^3.7.2",
|
||||||
|
"winston-daily-rotate-file": "^4.6.1",
|
||||||
"ws": "^8.4.2"
|
"ws": "^8.4.2"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
@@ -32,6 +36,24 @@
|
|||||||
"node": ">=6.9.0"
|
"node": ">=6.9.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@colors/colors": {
|
||||||
|
"version": "1.5.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.5.0.tgz",
|
||||||
|
"integrity": "sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.1.90"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@dabh/diagnostics": {
|
||||||
|
"version": "2.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/@dabh/diagnostics/-/diagnostics-2.0.3.tgz",
|
||||||
|
"integrity": "sha512-hrlQOIi7hAfzsMqlGSFyVucrx38O+j6wiGOf//H2ecvIEqYN4ADBSS2iLMh5UFyDunCNniUIPk/q3riFv45xRA==",
|
||||||
|
"dependencies": {
|
||||||
|
"colorspace": "1.1.x",
|
||||||
|
"enabled": "2.0.x",
|
||||||
|
"kuler": "^2.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@rollup/pluginutils": {
|
"node_modules/@rollup/pluginutils": {
|
||||||
"version": "4.2.0",
|
"version": "4.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-4.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-4.2.0.tgz",
|
||||||
@@ -73,6 +95,11 @@
|
|||||||
"node": "*"
|
"node": "*"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/async": {
|
||||||
|
"version": "3.2.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/async/-/async-3.2.3.tgz",
|
||||||
|
"integrity": "sha512-spZRyzKL5l5BZQrr/6m/SqFdBN0q3OCI0f9rjfBzCMBIP4p75P620rR3gTmaksNOhmzgdxcaxdNfMy6anrbM0g=="
|
||||||
|
},
|
||||||
"node_modules/base64-js": {
|
"node_modules/base64-js": {
|
||||||
"version": "1.5.1",
|
"version": "1.5.1",
|
||||||
"resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz",
|
"resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz",
|
||||||
@@ -152,6 +179,46 @@
|
|||||||
"node": "*"
|
"node": "*"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/color": {
|
||||||
|
"version": "3.2.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/color/-/color-3.2.1.tgz",
|
||||||
|
"integrity": "sha512-aBl7dZI9ENN6fUGC7mWpMTPNHmWUSNan9tuWN6ahh5ZLNk9baLJOnSMlrQkHcrfFgz2/RigjUVAjdx36VcemKA==",
|
||||||
|
"dependencies": {
|
||||||
|
"color-convert": "^1.9.3",
|
||||||
|
"color-string": "^1.6.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/color-convert": {
|
||||||
|
"version": "1.9.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
|
||||||
|
"integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==",
|
||||||
|
"dependencies": {
|
||||||
|
"color-name": "1.1.3"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/color-name": {
|
||||||
|
"version": "1.1.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
|
||||||
|
"integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU="
|
||||||
|
},
|
||||||
|
"node_modules/color-string": {
|
||||||
|
"version": "1.9.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/color-string/-/color-string-1.9.0.tgz",
|
||||||
|
"integrity": "sha512-9Mrz2AQLefkH1UvASKj6v6hj/7eWgjnT/cVsR8CumieLoT+g900exWeNogqtweI8dxloXN9BDQTYro1oWu/5CQ==",
|
||||||
|
"dependencies": {
|
||||||
|
"color-name": "^1.0.0",
|
||||||
|
"simple-swizzle": "^0.2.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/colorspace": {
|
||||||
|
"version": "1.1.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/colorspace/-/colorspace-1.1.4.tgz",
|
||||||
|
"integrity": "sha512-BgvKJiuVu1igBUF2kEjRCZXol6wiiGbY5ipL/oVPwm0BL9sIpMIzM8IK7vwuxIIzOXMV3Ey5w+vxhm0rR/TN8w==",
|
||||||
|
"dependencies": {
|
||||||
|
"color": "^3.1.3",
|
||||||
|
"text-hex": "1.0.x"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/connect-route": {
|
"node_modules/connect-route": {
|
||||||
"version": "0.1.5",
|
"version": "0.1.5",
|
||||||
"resolved": "https://registry.npmjs.org/connect-route/-/connect-route-0.1.5.tgz",
|
"resolved": "https://registry.npmjs.org/connect-route/-/connect-route-0.1.5.tgz",
|
||||||
@@ -180,12 +247,35 @@
|
|||||||
"node": ">=0.10"
|
"node": ">=0.10"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/enabled": {
|
||||||
|
"version": "2.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/enabled/-/enabled-2.0.0.tgz",
|
||||||
|
"integrity": "sha512-AKrN98kuwOzMIdAizXGI86UFBoo26CL21UM763y1h/GMSJ4/OHU9k2YlsmBpyScFo/wbLzWQJBMCW4+IO3/+OQ=="
|
||||||
|
},
|
||||||
"node_modules/estree-walker": {
|
"node_modules/estree-walker": {
|
||||||
"version": "2.0.2",
|
"version": "2.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz",
|
||||||
"integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==",
|
"integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"node_modules/fecha": {
|
||||||
|
"version": "4.2.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/fecha/-/fecha-4.2.1.tgz",
|
||||||
|
"integrity": "sha512-MMMQ0ludy/nBs1/o0zVOiKTpG7qMbonKUzjJgQFEuvq6INZ1OraKPRAWkBq5vlKLOUMpmNYG1JoN3oDPUQ9m3Q=="
|
||||||
|
},
|
||||||
|
"node_modules/file-stream-rotator": {
|
||||||
|
"version": "0.6.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/file-stream-rotator/-/file-stream-rotator-0.6.1.tgz",
|
||||||
|
"integrity": "sha512-u+dBid4PvZw17PmDeRcNOtCP9CCK/9lRN2w+r1xIS7yOL9JFrIBKTvrYsxT4P0pGtThYTn++QS5ChHaUov3+zQ==",
|
||||||
|
"dependencies": {
|
||||||
|
"moment": "^2.29.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/fn.name": {
|
||||||
|
"version": "1.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/fn.name/-/fn.name-1.1.0.tgz",
|
||||||
|
"integrity": "sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw=="
|
||||||
|
},
|
||||||
"node_modules/get-func-name": {
|
"node_modules/get-func-name": {
|
||||||
"version": "2.0.0",
|
"version": "2.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.0.tgz",
|
||||||
@@ -219,16 +309,54 @@
|
|||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
"node_modules/inherits": {
|
||||||
|
"version": "2.0.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
|
||||||
|
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
|
||||||
|
},
|
||||||
"node_modules/ip": {
|
"node_modules/ip": {
|
||||||
"version": "1.1.5",
|
"version": "1.1.5",
|
||||||
"resolved": "https://registry.npmjs.org/ip/-/ip-1.1.5.tgz",
|
"resolved": "https://registry.npmjs.org/ip/-/ip-1.1.5.tgz",
|
||||||
"integrity": "sha1-vd7XARQpCCjAoDnnLvJfWq7ENUo="
|
"integrity": "sha1-vd7XARQpCCjAoDnnLvJfWq7ENUo="
|
||||||
},
|
},
|
||||||
|
"node_modules/is-arrayish": {
|
||||||
|
"version": "0.3.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz",
|
||||||
|
"integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ=="
|
||||||
|
},
|
||||||
|
"node_modules/is-stream": {
|
||||||
|
"version": "2.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz",
|
||||||
|
"integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=8"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/sindresorhus"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/jquery": {
|
"node_modules/jquery": {
|
||||||
"version": "3.6.0",
|
"version": "3.6.0",
|
||||||
"resolved": "https://registry.npmjs.org/jquery/-/jquery-3.6.0.tgz",
|
"resolved": "https://registry.npmjs.org/jquery/-/jquery-3.6.0.tgz",
|
||||||
"integrity": "sha512-JVzAR/AjBvVt2BmYhxRCSYysDsPcssdmTFnzyLEts9qNwmjmu4JTAMYubEfwVOSwpQ1I1sKKFcxhZCI2buerfw=="
|
"integrity": "sha512-JVzAR/AjBvVt2BmYhxRCSYysDsPcssdmTFnzyLEts9qNwmjmu4JTAMYubEfwVOSwpQ1I1sKKFcxhZCI2buerfw=="
|
||||||
},
|
},
|
||||||
|
"node_modules/kuler": {
|
||||||
|
"version": "2.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/kuler/-/kuler-2.0.0.tgz",
|
||||||
|
"integrity": "sha512-Xq9nH7KlWZmXAtodXDDRE7vs6DU1gTU8zYDHDiWLSip45Egwq3plLHzPn27NgvzL2r1LMPC1vdqh98sQxtqj4A=="
|
||||||
|
},
|
||||||
|
"node_modules/logform": {
|
||||||
|
"version": "2.4.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/logform/-/logform-2.4.0.tgz",
|
||||||
|
"integrity": "sha512-CPSJw4ftjf517EhXZGGvTHHkYobo7ZCc0kvwUoOYcjfR2UVrI66RHj8MCrfAdEitdmFqbu2BYdYs8FHHZSb6iw==",
|
||||||
|
"dependencies": {
|
||||||
|
"@colors/colors": "1.5.0",
|
||||||
|
"fecha": "^4.2.0",
|
||||||
|
"ms": "^2.1.1",
|
||||||
|
"safe-stable-stringify": "^2.3.1",
|
||||||
|
"triple-beam": "^1.3.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/memory-pager": {
|
"node_modules/memory-pager": {
|
||||||
"version": "1.5.0",
|
"version": "1.5.0",
|
||||||
"resolved": "https://registry.npmjs.org/memory-pager/-/memory-pager-1.5.0.tgz",
|
"resolved": "https://registry.npmjs.org/memory-pager/-/memory-pager-1.5.0.tgz",
|
||||||
@@ -919,6 +1047,14 @@
|
|||||||
"node": ">=0.4"
|
"node": ">=0.4"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/moment": {
|
||||||
|
"version": "2.29.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/moment/-/moment-2.29.2.tgz",
|
||||||
|
"integrity": "sha512-UgzG4rvxYpN15jgCmVJwac49h9ly9NurikMWGPdVxm8GZD6XjkKPxDTjQQ43gtGgnV3X0cAyWDdP2Wexoquifg==",
|
||||||
|
"engines": {
|
||||||
|
"node": "*"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/mongodb": {
|
"node_modules/mongodb": {
|
||||||
"version": "4.4.1",
|
"version": "4.4.1",
|
||||||
"resolved": "https://registry.npmjs.org/mongodb/-/mongodb-4.4.1.tgz",
|
"resolved": "https://registry.npmjs.org/mongodb/-/mongodb-4.4.1.tgz",
|
||||||
@@ -945,6 +1081,27 @@
|
|||||||
"whatwg-url": "^11.0.0"
|
"whatwg-url": "^11.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/ms": {
|
||||||
|
"version": "2.1.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
|
||||||
|
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="
|
||||||
|
},
|
||||||
|
"node_modules/object-hash": {
|
||||||
|
"version": "2.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/object-hash/-/object-hash-2.2.0.tgz",
|
||||||
|
"integrity": "sha512-gScRMn0bS5fH+IuwyIFgnh9zBdo4DV+6GhygmWM9HyNJSgS0hScp1f5vjtm7oIIOiT9trXrShAkLFSc2IqKNgw==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 6"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/one-time": {
|
||||||
|
"version": "1.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/one-time/-/one-time-1.0.0.tgz",
|
||||||
|
"integrity": "sha512-5DXOiRKwuSEcQ/l0kGCF6Q3jcADFv5tSmRaJck/OqkVFcOzutB134KRSfF0xDrL39MNnqxbHBbUUcjZIhTgb2g==",
|
||||||
|
"dependencies": {
|
||||||
|
"fn.name": "1.x.x"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/pathval": {
|
"node_modules/pathval": {
|
||||||
"version": "1.1.1",
|
"version": "1.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.1.tgz",
|
||||||
@@ -974,6 +1131,19 @@
|
|||||||
"node": ">=6"
|
"node": ">=6"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/readable-stream": {
|
||||||
|
"version": "3.6.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz",
|
||||||
|
"integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==",
|
||||||
|
"dependencies": {
|
||||||
|
"inherits": "^2.0.3",
|
||||||
|
"string_decoder": "^1.1.1",
|
||||||
|
"util-deprecate": "^1.0.1"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 6"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/regenerator-runtime": {
|
"node_modules/regenerator-runtime": {
|
||||||
"version": "0.13.9",
|
"version": "0.13.9",
|
||||||
"resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.9.tgz",
|
"resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.9.tgz",
|
||||||
@@ -994,6 +1164,33 @@
|
|||||||
"rollup": "1 || 2"
|
"rollup": "1 || 2"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/safe-buffer": {
|
||||||
|
"version": "5.2.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
|
||||||
|
"integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
|
||||||
|
"funding": [
|
||||||
|
{
|
||||||
|
"type": "github",
|
||||||
|
"url": "https://github.com/sponsors/feross"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "patreon",
|
||||||
|
"url": "https://www.patreon.com/feross"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "consulting",
|
||||||
|
"url": "https://feross.org/support"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"node_modules/safe-stable-stringify": {
|
||||||
|
"version": "2.3.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/safe-stable-stringify/-/safe-stable-stringify-2.3.1.tgz",
|
||||||
|
"integrity": "sha512-kYBSfT+troD9cDA85VDnHZ1rpHC50O0g1e6WlGHVCz/g+JS+9WKLj+XwFYyR8UbrZN8ll9HUpDAAddY58MGisg==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/saslprep": {
|
"node_modules/saslprep": {
|
||||||
"version": "1.0.3",
|
"version": "1.0.3",
|
||||||
"resolved": "https://registry.npmjs.org/saslprep/-/saslprep-1.0.3.tgz",
|
"resolved": "https://registry.npmjs.org/saslprep/-/saslprep-1.0.3.tgz",
|
||||||
@@ -1006,6 +1203,14 @@
|
|||||||
"node": ">=6"
|
"node": ">=6"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/simple-swizzle": {
|
||||||
|
"version": "0.2.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz",
|
||||||
|
"integrity": "sha1-pNprY1/8zMoz9w0Xy5JZLeleVXo=",
|
||||||
|
"dependencies": {
|
||||||
|
"is-arrayish": "^0.3.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/smart-buffer": {
|
"node_modules/smart-buffer": {
|
||||||
"version": "4.2.0",
|
"version": "4.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz",
|
||||||
@@ -1037,6 +1242,22 @@
|
|||||||
"memory-pager": "^1.0.2"
|
"memory-pager": "^1.0.2"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/stack-trace": {
|
||||||
|
"version": "0.0.10",
|
||||||
|
"resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.10.tgz",
|
||||||
|
"integrity": "sha1-VHxws0fo0ytOEI6hoqFZ5f3eGcA=",
|
||||||
|
"engines": {
|
||||||
|
"node": "*"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/string_decoder": {
|
||||||
|
"version": "1.3.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz",
|
||||||
|
"integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==",
|
||||||
|
"dependencies": {
|
||||||
|
"safe-buffer": "~5.2.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/svelte": {
|
"node_modules/svelte": {
|
||||||
"version": "3.46.4",
|
"version": "3.46.4",
|
||||||
"resolved": "https://registry.npmjs.org/svelte/-/svelte-3.46.4.tgz",
|
"resolved": "https://registry.npmjs.org/svelte/-/svelte-3.46.4.tgz",
|
||||||
@@ -1045,6 +1266,11 @@
|
|||||||
"node": ">= 8"
|
"node": ">= 8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/text-hex": {
|
||||||
|
"version": "1.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/text-hex/-/text-hex-1.0.0.tgz",
|
||||||
|
"integrity": "sha512-uuVGNWzgJ4yhRaNSiubPY7OjISw4sw4E5Uv0wbjp+OzcbmVU/rsT8ujgcXJhn9ypzsgr5vlzpPqP+MBBKcGvbg=="
|
||||||
|
},
|
||||||
"node_modules/tinro": {
|
"node_modules/tinro": {
|
||||||
"version": "0.6.12",
|
"version": "0.6.12",
|
||||||
"resolved": "https://registry.npmjs.org/tinro/-/tinro-0.6.12.tgz",
|
"resolved": "https://registry.npmjs.org/tinro/-/tinro-0.6.12.tgz",
|
||||||
@@ -1061,6 +1287,11 @@
|
|||||||
"node": ">=12"
|
"node": ">=12"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/triple-beam": {
|
||||||
|
"version": "1.3.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/triple-beam/-/triple-beam-1.3.0.tgz",
|
||||||
|
"integrity": "sha512-XrHUvV5HpdLmIj4uVMxHggLbFSZYIn7HEWsqePZcI50pco+MPqJ50wMGY794X7AOOhxOBAjbkqfAbEe/QMp2Lw=="
|
||||||
|
},
|
||||||
"node_modules/type-detect": {
|
"node_modules/type-detect": {
|
||||||
"version": "4.0.8",
|
"version": "4.0.8",
|
||||||
"resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz",
|
"resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz",
|
||||||
@@ -1070,6 +1301,16 @@
|
|||||||
"node": ">=4"
|
"node": ">=4"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/underscore": {
|
||||||
|
"version": "1.13.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/underscore/-/underscore-1.13.2.tgz",
|
||||||
|
"integrity": "sha512-ekY1NhRzq0B08g4bGuX4wd2jZx5GnKz6mKSqFL4nqBlfyMGiG10gDFhDTMEfYmDL6Jy0FUIZp7wiRB+0BP7J2g=="
|
||||||
|
},
|
||||||
|
"node_modules/util-deprecate": {
|
||||||
|
"version": "1.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
|
||||||
|
"integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8="
|
||||||
|
},
|
||||||
"node_modules/webidl-conversions": {
|
"node_modules/webidl-conversions": {
|
||||||
"version": "7.0.0",
|
"version": "7.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz",
|
||||||
@@ -1090,6 +1331,56 @@
|
|||||||
"node": ">=12"
|
"node": ">=12"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/winston": {
|
||||||
|
"version": "3.7.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/winston/-/winston-3.7.2.tgz",
|
||||||
|
"integrity": "sha512-QziIqtojHBoyzUOdQvQiar1DH0Xp9nF1A1y7NVy2DGEsz82SBDtOalS0ulTRGVT14xPX3WRWkCsdcJKqNflKng==",
|
||||||
|
"dependencies": {
|
||||||
|
"@dabh/diagnostics": "^2.0.2",
|
||||||
|
"async": "^3.2.3",
|
||||||
|
"is-stream": "^2.0.0",
|
||||||
|
"logform": "^2.4.0",
|
||||||
|
"one-time": "^1.0.0",
|
||||||
|
"readable-stream": "^3.4.0",
|
||||||
|
"safe-stable-stringify": "^2.3.1",
|
||||||
|
"stack-trace": "0.0.x",
|
||||||
|
"triple-beam": "^1.3.0",
|
||||||
|
"winston-transport": "^4.5.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 12.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/winston-daily-rotate-file": {
|
||||||
|
"version": "4.6.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/winston-daily-rotate-file/-/winston-daily-rotate-file-4.6.1.tgz",
|
||||||
|
"integrity": "sha512-Ycch4LZmTycbhgiI2eQXBKI1pKcEQgAqmBjyq7/dC6Dk77nasdxvhLKraqTdCw7wNDSs8/M0jXaLATHquG7xYg==",
|
||||||
|
"dependencies": {
|
||||||
|
"file-stream-rotator": "^0.6.1",
|
||||||
|
"object-hash": "^2.0.1",
|
||||||
|
"triple-beam": "^1.3.0",
|
||||||
|
"winston-transport": "^4.4.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=8"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"winston": "^3"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/winston-transport": {
|
||||||
|
"version": "4.5.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/winston-transport/-/winston-transport-4.5.0.tgz",
|
||||||
|
"integrity": "sha512-YpZzcUzBedhlTAfJg6vJDlyEai/IFMIVcaEZZyl3UXIl4gmqRpU7AE89AHLkbzLUsv0NVmw7ts+iztqKxxPW1Q==",
|
||||||
|
"dependencies": {
|
||||||
|
"logform": "^2.3.2",
|
||||||
|
"readable-stream": "^3.6.0",
|
||||||
|
"triple-beam": "^1.3.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 6.4.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/ws": {
|
"node_modules/ws": {
|
||||||
"version": "8.4.2",
|
"version": "8.4.2",
|
||||||
"resolved": "https://registry.npmjs.org/ws/-/ws-8.4.2.tgz",
|
"resolved": "https://registry.npmjs.org/ws/-/ws-8.4.2.tgz",
|
||||||
@@ -1120,6 +1411,21 @@
|
|||||||
"regenerator-runtime": "^0.13.4"
|
"regenerator-runtime": "^0.13.4"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"@colors/colors": {
|
||||||
|
"version": "1.5.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.5.0.tgz",
|
||||||
|
"integrity": "sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ=="
|
||||||
|
},
|
||||||
|
"@dabh/diagnostics": {
|
||||||
|
"version": "2.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/@dabh/diagnostics/-/diagnostics-2.0.3.tgz",
|
||||||
|
"integrity": "sha512-hrlQOIi7hAfzsMqlGSFyVucrx38O+j6wiGOf//H2ecvIEqYN4ADBSS2iLMh5UFyDunCNniUIPk/q3riFv45xRA==",
|
||||||
|
"requires": {
|
||||||
|
"colorspace": "1.1.x",
|
||||||
|
"enabled": "2.0.x",
|
||||||
|
"kuler": "^2.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"@rollup/pluginutils": {
|
"@rollup/pluginutils": {
|
||||||
"version": "4.2.0",
|
"version": "4.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-4.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-4.2.0.tgz",
|
||||||
@@ -1155,6 +1461,11 @@
|
|||||||
"integrity": "sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==",
|
"integrity": "sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"async": {
|
||||||
|
"version": "3.2.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/async/-/async-3.2.3.tgz",
|
||||||
|
"integrity": "sha512-spZRyzKL5l5BZQrr/6m/SqFdBN0q3OCI0f9rjfBzCMBIP4p75P620rR3gTmaksNOhmzgdxcaxdNfMy6anrbM0g=="
|
||||||
|
},
|
||||||
"base64-js": {
|
"base64-js": {
|
||||||
"version": "1.5.1",
|
"version": "1.5.1",
|
||||||
"resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz",
|
"resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz",
|
||||||
@@ -1197,6 +1508,46 @@
|
|||||||
"integrity": "sha1-V00xLt2Iu13YkS6Sht1sCu1KrII=",
|
"integrity": "sha1-V00xLt2Iu13YkS6Sht1sCu1KrII=",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"color": {
|
||||||
|
"version": "3.2.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/color/-/color-3.2.1.tgz",
|
||||||
|
"integrity": "sha512-aBl7dZI9ENN6fUGC7mWpMTPNHmWUSNan9tuWN6ahh5ZLNk9baLJOnSMlrQkHcrfFgz2/RigjUVAjdx36VcemKA==",
|
||||||
|
"requires": {
|
||||||
|
"color-convert": "^1.9.3",
|
||||||
|
"color-string": "^1.6.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"color-convert": {
|
||||||
|
"version": "1.9.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
|
||||||
|
"integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==",
|
||||||
|
"requires": {
|
||||||
|
"color-name": "1.1.3"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"color-name": {
|
||||||
|
"version": "1.1.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
|
||||||
|
"integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU="
|
||||||
|
},
|
||||||
|
"color-string": {
|
||||||
|
"version": "1.9.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/color-string/-/color-string-1.9.0.tgz",
|
||||||
|
"integrity": "sha512-9Mrz2AQLefkH1UvASKj6v6hj/7eWgjnT/cVsR8CumieLoT+g900exWeNogqtweI8dxloXN9BDQTYro1oWu/5CQ==",
|
||||||
|
"requires": {
|
||||||
|
"color-name": "^1.0.0",
|
||||||
|
"simple-swizzle": "^0.2.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"colorspace": {
|
||||||
|
"version": "1.1.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/colorspace/-/colorspace-1.1.4.tgz",
|
||||||
|
"integrity": "sha512-BgvKJiuVu1igBUF2kEjRCZXol6wiiGbY5ipL/oVPwm0BL9sIpMIzM8IK7vwuxIIzOXMV3Ey5w+vxhm0rR/TN8w==",
|
||||||
|
"requires": {
|
||||||
|
"color": "^3.1.3",
|
||||||
|
"text-hex": "1.0.x"
|
||||||
|
}
|
||||||
|
},
|
||||||
"connect-route": {
|
"connect-route": {
|
||||||
"version": "0.1.5",
|
"version": "0.1.5",
|
||||||
"resolved": "https://registry.npmjs.org/connect-route/-/connect-route-0.1.5.tgz",
|
"resolved": "https://registry.npmjs.org/connect-route/-/connect-route-0.1.5.tgz",
|
||||||
@@ -1216,12 +1567,35 @@
|
|||||||
"resolved": "https://registry.npmjs.org/denque/-/denque-2.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/denque/-/denque-2.0.1.tgz",
|
||||||
"integrity": "sha512-tfiWc6BQLXNLpNiR5iGd0Ocu3P3VpxfzFiqubLgMfhfOw9WyvgJBd46CClNn9k3qfbjvT//0cf7AlYRX/OslMQ=="
|
"integrity": "sha512-tfiWc6BQLXNLpNiR5iGd0Ocu3P3VpxfzFiqubLgMfhfOw9WyvgJBd46CClNn9k3qfbjvT//0cf7AlYRX/OslMQ=="
|
||||||
},
|
},
|
||||||
|
"enabled": {
|
||||||
|
"version": "2.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/enabled/-/enabled-2.0.0.tgz",
|
||||||
|
"integrity": "sha512-AKrN98kuwOzMIdAizXGI86UFBoo26CL21UM763y1h/GMSJ4/OHU9k2YlsmBpyScFo/wbLzWQJBMCW4+IO3/+OQ=="
|
||||||
|
},
|
||||||
"estree-walker": {
|
"estree-walker": {
|
||||||
"version": "2.0.2",
|
"version": "2.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz",
|
||||||
"integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==",
|
"integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"fecha": {
|
||||||
|
"version": "4.2.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/fecha/-/fecha-4.2.1.tgz",
|
||||||
|
"integrity": "sha512-MMMQ0ludy/nBs1/o0zVOiKTpG7qMbonKUzjJgQFEuvq6INZ1OraKPRAWkBq5vlKLOUMpmNYG1JoN3oDPUQ9m3Q=="
|
||||||
|
},
|
||||||
|
"file-stream-rotator": {
|
||||||
|
"version": "0.6.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/file-stream-rotator/-/file-stream-rotator-0.6.1.tgz",
|
||||||
|
"integrity": "sha512-u+dBid4PvZw17PmDeRcNOtCP9CCK/9lRN2w+r1xIS7yOL9JFrIBKTvrYsxT4P0pGtThYTn++QS5ChHaUov3+zQ==",
|
||||||
|
"requires": {
|
||||||
|
"moment": "^2.29.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"fn.name": {
|
||||||
|
"version": "1.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/fn.name/-/fn.name-1.1.0.tgz",
|
||||||
|
"integrity": "sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw=="
|
||||||
|
},
|
||||||
"get-func-name": {
|
"get-func-name": {
|
||||||
"version": "2.0.0",
|
"version": "2.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.0.tgz",
|
||||||
@@ -1238,16 +1612,48 @@
|
|||||||
"resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz",
|
"resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz",
|
||||||
"integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA=="
|
"integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA=="
|
||||||
},
|
},
|
||||||
|
"inherits": {
|
||||||
|
"version": "2.0.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
|
||||||
|
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
|
||||||
|
},
|
||||||
"ip": {
|
"ip": {
|
||||||
"version": "1.1.5",
|
"version": "1.1.5",
|
||||||
"resolved": "https://registry.npmjs.org/ip/-/ip-1.1.5.tgz",
|
"resolved": "https://registry.npmjs.org/ip/-/ip-1.1.5.tgz",
|
||||||
"integrity": "sha1-vd7XARQpCCjAoDnnLvJfWq7ENUo="
|
"integrity": "sha1-vd7XARQpCCjAoDnnLvJfWq7ENUo="
|
||||||
},
|
},
|
||||||
|
"is-arrayish": {
|
||||||
|
"version": "0.3.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz",
|
||||||
|
"integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ=="
|
||||||
|
},
|
||||||
|
"is-stream": {
|
||||||
|
"version": "2.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz",
|
||||||
|
"integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg=="
|
||||||
|
},
|
||||||
"jquery": {
|
"jquery": {
|
||||||
"version": "3.6.0",
|
"version": "3.6.0",
|
||||||
"resolved": "https://registry.npmjs.org/jquery/-/jquery-3.6.0.tgz",
|
"resolved": "https://registry.npmjs.org/jquery/-/jquery-3.6.0.tgz",
|
||||||
"integrity": "sha512-JVzAR/AjBvVt2BmYhxRCSYysDsPcssdmTFnzyLEts9qNwmjmu4JTAMYubEfwVOSwpQ1I1sKKFcxhZCI2buerfw=="
|
"integrity": "sha512-JVzAR/AjBvVt2BmYhxRCSYysDsPcssdmTFnzyLEts9qNwmjmu4JTAMYubEfwVOSwpQ1I1sKKFcxhZCI2buerfw=="
|
||||||
},
|
},
|
||||||
|
"kuler": {
|
||||||
|
"version": "2.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/kuler/-/kuler-2.0.0.tgz",
|
||||||
|
"integrity": "sha512-Xq9nH7KlWZmXAtodXDDRE7vs6DU1gTU8zYDHDiWLSip45Egwq3plLHzPn27NgvzL2r1LMPC1vdqh98sQxtqj4A=="
|
||||||
|
},
|
||||||
|
"logform": {
|
||||||
|
"version": "2.4.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/logform/-/logform-2.4.0.tgz",
|
||||||
|
"integrity": "sha512-CPSJw4ftjf517EhXZGGvTHHkYobo7ZCc0kvwUoOYcjfR2UVrI66RHj8MCrfAdEitdmFqbu2BYdYs8FHHZSb6iw==",
|
||||||
|
"requires": {
|
||||||
|
"@colors/colors": "1.5.0",
|
||||||
|
"fecha": "^4.2.0",
|
||||||
|
"ms": "^2.1.1",
|
||||||
|
"safe-stable-stringify": "^2.3.1",
|
||||||
|
"triple-beam": "^1.3.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"memory-pager": {
|
"memory-pager": {
|
||||||
"version": "1.5.0",
|
"version": "1.5.0",
|
||||||
"resolved": "https://registry.npmjs.org/memory-pager/-/memory-pager-1.5.0.tgz",
|
"resolved": "https://registry.npmjs.org/memory-pager/-/memory-pager-1.5.0.tgz",
|
||||||
@@ -1815,6 +2221,11 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"moment": {
|
||||||
|
"version": "2.29.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/moment/-/moment-2.29.2.tgz",
|
||||||
|
"integrity": "sha512-UgzG4rvxYpN15jgCmVJwac49h9ly9NurikMWGPdVxm8GZD6XjkKPxDTjQQ43gtGgnV3X0cAyWDdP2Wexoquifg=="
|
||||||
|
},
|
||||||
"mongodb": {
|
"mongodb": {
|
||||||
"version": "4.4.1",
|
"version": "4.4.1",
|
||||||
"resolved": "https://registry.npmjs.org/mongodb/-/mongodb-4.4.1.tgz",
|
"resolved": "https://registry.npmjs.org/mongodb/-/mongodb-4.4.1.tgz",
|
||||||
@@ -1836,6 +2247,24 @@
|
|||||||
"whatwg-url": "^11.0.0"
|
"whatwg-url": "^11.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"ms": {
|
||||||
|
"version": "2.1.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
|
||||||
|
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="
|
||||||
|
},
|
||||||
|
"object-hash": {
|
||||||
|
"version": "2.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/object-hash/-/object-hash-2.2.0.tgz",
|
||||||
|
"integrity": "sha512-gScRMn0bS5fH+IuwyIFgnh9zBdo4DV+6GhygmWM9HyNJSgS0hScp1f5vjtm7oIIOiT9trXrShAkLFSc2IqKNgw=="
|
||||||
|
},
|
||||||
|
"one-time": {
|
||||||
|
"version": "1.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/one-time/-/one-time-1.0.0.tgz",
|
||||||
|
"integrity": "sha512-5DXOiRKwuSEcQ/l0kGCF6Q3jcADFv5tSmRaJck/OqkVFcOzutB134KRSfF0xDrL39MNnqxbHBbUUcjZIhTgb2g==",
|
||||||
|
"requires": {
|
||||||
|
"fn.name": "1.x.x"
|
||||||
|
}
|
||||||
|
},
|
||||||
"pathval": {
|
"pathval": {
|
||||||
"version": "1.1.1",
|
"version": "1.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.1.tgz",
|
||||||
@@ -1853,6 +2282,16 @@
|
|||||||
"resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz",
|
||||||
"integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A=="
|
"integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A=="
|
||||||
},
|
},
|
||||||
|
"readable-stream": {
|
||||||
|
"version": "3.6.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz",
|
||||||
|
"integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==",
|
||||||
|
"requires": {
|
||||||
|
"inherits": "^2.0.3",
|
||||||
|
"string_decoder": "^1.1.1",
|
||||||
|
"util-deprecate": "^1.0.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
"regenerator-runtime": {
|
"regenerator-runtime": {
|
||||||
"version": "0.13.9",
|
"version": "0.13.9",
|
||||||
"resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.9.tgz",
|
"resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.9.tgz",
|
||||||
@@ -1867,6 +2306,16 @@
|
|||||||
"@rollup/pluginutils": "4"
|
"@rollup/pluginutils": "4"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"safe-buffer": {
|
||||||
|
"version": "5.2.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
|
||||||
|
"integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ=="
|
||||||
|
},
|
||||||
|
"safe-stable-stringify": {
|
||||||
|
"version": "2.3.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/safe-stable-stringify/-/safe-stable-stringify-2.3.1.tgz",
|
||||||
|
"integrity": "sha512-kYBSfT+troD9cDA85VDnHZ1rpHC50O0g1e6WlGHVCz/g+JS+9WKLj+XwFYyR8UbrZN8ll9HUpDAAddY58MGisg=="
|
||||||
|
},
|
||||||
"saslprep": {
|
"saslprep": {
|
||||||
"version": "1.0.3",
|
"version": "1.0.3",
|
||||||
"resolved": "https://registry.npmjs.org/saslprep/-/saslprep-1.0.3.tgz",
|
"resolved": "https://registry.npmjs.org/saslprep/-/saslprep-1.0.3.tgz",
|
||||||
@@ -1876,6 +2325,14 @@
|
|||||||
"sparse-bitfield": "^3.0.3"
|
"sparse-bitfield": "^3.0.3"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"simple-swizzle": {
|
||||||
|
"version": "0.2.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz",
|
||||||
|
"integrity": "sha1-pNprY1/8zMoz9w0Xy5JZLeleVXo=",
|
||||||
|
"requires": {
|
||||||
|
"is-arrayish": "^0.3.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
"smart-buffer": {
|
"smart-buffer": {
|
||||||
"version": "4.2.0",
|
"version": "4.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz",
|
||||||
@@ -1899,11 +2356,29 @@
|
|||||||
"memory-pager": "^1.0.2"
|
"memory-pager": "^1.0.2"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"stack-trace": {
|
||||||
|
"version": "0.0.10",
|
||||||
|
"resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.10.tgz",
|
||||||
|
"integrity": "sha1-VHxws0fo0ytOEI6hoqFZ5f3eGcA="
|
||||||
|
},
|
||||||
|
"string_decoder": {
|
||||||
|
"version": "1.3.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz",
|
||||||
|
"integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==",
|
||||||
|
"requires": {
|
||||||
|
"safe-buffer": "~5.2.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"svelte": {
|
"svelte": {
|
||||||
"version": "3.46.4",
|
"version": "3.46.4",
|
||||||
"resolved": "https://registry.npmjs.org/svelte/-/svelte-3.46.4.tgz",
|
"resolved": "https://registry.npmjs.org/svelte/-/svelte-3.46.4.tgz",
|
||||||
"integrity": "sha512-qKJzw6DpA33CIa+C/rGp4AUdSfii0DOTCzj/2YpSKKayw5WGSS624Et9L1nU1k2OVRS9vaENQXp2CVZNU+xvIg=="
|
"integrity": "sha512-qKJzw6DpA33CIa+C/rGp4AUdSfii0DOTCzj/2YpSKKayw5WGSS624Et9L1nU1k2OVRS9vaENQXp2CVZNU+xvIg=="
|
||||||
},
|
},
|
||||||
|
"text-hex": {
|
||||||
|
"version": "1.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/text-hex/-/text-hex-1.0.0.tgz",
|
||||||
|
"integrity": "sha512-uuVGNWzgJ4yhRaNSiubPY7OjISw4sw4E5Uv0wbjp+OzcbmVU/rsT8ujgcXJhn9ypzsgr5vlzpPqP+MBBKcGvbg=="
|
||||||
|
},
|
||||||
"tinro": {
|
"tinro": {
|
||||||
"version": "0.6.12",
|
"version": "0.6.12",
|
||||||
"resolved": "https://registry.npmjs.org/tinro/-/tinro-0.6.12.tgz",
|
"resolved": "https://registry.npmjs.org/tinro/-/tinro-0.6.12.tgz",
|
||||||
@@ -1917,12 +2392,27 @@
|
|||||||
"punycode": "^2.1.1"
|
"punycode": "^2.1.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"triple-beam": {
|
||||||
|
"version": "1.3.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/triple-beam/-/triple-beam-1.3.0.tgz",
|
||||||
|
"integrity": "sha512-XrHUvV5HpdLmIj4uVMxHggLbFSZYIn7HEWsqePZcI50pco+MPqJ50wMGY794X7AOOhxOBAjbkqfAbEe/QMp2Lw=="
|
||||||
|
},
|
||||||
"type-detect": {
|
"type-detect": {
|
||||||
"version": "4.0.8",
|
"version": "4.0.8",
|
||||||
"resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz",
|
"resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz",
|
||||||
"integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==",
|
"integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"underscore": {
|
||||||
|
"version": "1.13.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/underscore/-/underscore-1.13.2.tgz",
|
||||||
|
"integrity": "sha512-ekY1NhRzq0B08g4bGuX4wd2jZx5GnKz6mKSqFL4nqBlfyMGiG10gDFhDTMEfYmDL6Jy0FUIZp7wiRB+0BP7J2g=="
|
||||||
|
},
|
||||||
|
"util-deprecate": {
|
||||||
|
"version": "1.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
|
||||||
|
"integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8="
|
||||||
|
},
|
||||||
"webidl-conversions": {
|
"webidl-conversions": {
|
||||||
"version": "7.0.0",
|
"version": "7.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz",
|
||||||
@@ -1937,6 +2427,44 @@
|
|||||||
"webidl-conversions": "^7.0.0"
|
"webidl-conversions": "^7.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"winston": {
|
||||||
|
"version": "3.7.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/winston/-/winston-3.7.2.tgz",
|
||||||
|
"integrity": "sha512-QziIqtojHBoyzUOdQvQiar1DH0Xp9nF1A1y7NVy2DGEsz82SBDtOalS0ulTRGVT14xPX3WRWkCsdcJKqNflKng==",
|
||||||
|
"requires": {
|
||||||
|
"@dabh/diagnostics": "^2.0.2",
|
||||||
|
"async": "^3.2.3",
|
||||||
|
"is-stream": "^2.0.0",
|
||||||
|
"logform": "^2.4.0",
|
||||||
|
"one-time": "^1.0.0",
|
||||||
|
"readable-stream": "^3.4.0",
|
||||||
|
"safe-stable-stringify": "^2.3.1",
|
||||||
|
"stack-trace": "0.0.x",
|
||||||
|
"triple-beam": "^1.3.0",
|
||||||
|
"winston-transport": "^4.5.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"winston-daily-rotate-file": {
|
||||||
|
"version": "4.6.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/winston-daily-rotate-file/-/winston-daily-rotate-file-4.6.1.tgz",
|
||||||
|
"integrity": "sha512-Ycch4LZmTycbhgiI2eQXBKI1pKcEQgAqmBjyq7/dC6Dk77nasdxvhLKraqTdCw7wNDSs8/M0jXaLATHquG7xYg==",
|
||||||
|
"requires": {
|
||||||
|
"file-stream-rotator": "^0.6.1",
|
||||||
|
"object-hash": "^2.0.1",
|
||||||
|
"triple-beam": "^1.3.0",
|
||||||
|
"winston-transport": "^4.4.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"winston-transport": {
|
||||||
|
"version": "4.5.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/winston-transport/-/winston-transport-4.5.0.tgz",
|
||||||
|
"integrity": "sha512-YpZzcUzBedhlTAfJg6vJDlyEai/IFMIVcaEZZyl3UXIl4gmqRpU7AE89AHLkbzLUsv0NVmw7ts+iztqKxxPW1Q==",
|
||||||
|
"requires": {
|
||||||
|
"logform": "^2.3.2",
|
||||||
|
"readable-stream": "^3.6.0",
|
||||||
|
"triple-beam": "^1.3.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"ws": {
|
"ws": {
|
||||||
"version": "8.4.2",
|
"version": "8.4.2",
|
||||||
"resolved": "https://registry.npmjs.org/ws/-/ws-8.4.2.tgz",
|
"resolved": "https://registry.npmjs.org/ws/-/ws-8.4.2.tgz",
|
||||||
|
|||||||
@@ -14,9 +14,13 @@
|
|||||||
"html5-qrcode": "^2.2.0",
|
"html5-qrcode": "^2.2.0",
|
||||||
"jquery": "^3.6.0",
|
"jquery": "^3.6.0",
|
||||||
"meteor-node-stubs": "^1.0.0",
|
"meteor-node-stubs": "^1.0.0",
|
||||||
|
"moment": "^2.29.2",
|
||||||
"mongodb": "^4.4.1",
|
"mongodb": "^4.4.1",
|
||||||
"svelte": "^3.46.4",
|
"svelte": "^3.46.4",
|
||||||
"tinro": "^0.6.12",
|
"tinro": "^0.6.12",
|
||||||
|
"underscore": "^1.13.2",
|
||||||
|
"winston": "^3.7.2",
|
||||||
|
"winston-daily-rotate-file": "^4.6.1",
|
||||||
"ws": "^8.4.2"
|
"ws": "^8.4.2"
|
||||||
},
|
},
|
||||||
"meteor": {
|
"meteor": {
|
||||||
|
|||||||
BIN
public/fonts/Courgette-Regular.ttf
Normal file
BIN
public/fonts/Courgette-Regular.ttf
Normal file
Binary file not shown.
BIN
public/fonts/KaushanScript-Regular.ttf
Normal file
BIN
public/fonts/KaushanScript-Regular.ttf
Normal file
Binary file not shown.
BIN
public/fonts/Merienda-Bold.ttf
Normal file
BIN
public/fonts/Merienda-Bold.ttf
Normal file
Binary file not shown.
BIN
public/fonts/Merienda-Regular.ttf
Normal file
BIN
public/fonts/Merienda-Regular.ttf
Normal file
Binary file not shown.
BIN
public/fonts/Monoton-Regular.ttf
Normal file
BIN
public/fonts/Monoton-Regular.ttf
Normal file
Binary file not shown.
BIN
public/fonts/Spectral-Bold.ttf
Normal file
BIN
public/fonts/Spectral-Bold.ttf
Normal file
Binary file not shown.
BIN
public/fonts/Spectral-BoldItalic.ttf
Normal file
BIN
public/fonts/Spectral-BoldItalic.ttf
Normal file
Binary file not shown.
BIN
public/fonts/Spectral-ExtraBold.ttf
Normal file
BIN
public/fonts/Spectral-ExtraBold.ttf
Normal file
Binary file not shown.
BIN
public/fonts/Spectral-ExtraBoldItalic.ttf
Normal file
BIN
public/fonts/Spectral-ExtraBoldItalic.ttf
Normal file
Binary file not shown.
BIN
public/fonts/Spectral-ExtraLight.ttf
Normal file
BIN
public/fonts/Spectral-ExtraLight.ttf
Normal file
Binary file not shown.
BIN
public/fonts/Spectral-ExtraLightItalic.ttf
Normal file
BIN
public/fonts/Spectral-ExtraLightItalic.ttf
Normal file
Binary file not shown.
BIN
public/fonts/Spectral-Italic.ttf
Normal file
BIN
public/fonts/Spectral-Italic.ttf
Normal file
Binary file not shown.
BIN
public/fonts/Spectral-Light.ttf
Normal file
BIN
public/fonts/Spectral-Light.ttf
Normal file
Binary file not shown.
BIN
public/fonts/Spectral-LightItalic.ttf
Normal file
BIN
public/fonts/Spectral-LightItalic.ttf
Normal file
Binary file not shown.
BIN
public/fonts/Spectral-Medium.ttf
Normal file
BIN
public/fonts/Spectral-Medium.ttf
Normal file
Binary file not shown.
BIN
public/fonts/Spectral-MediumItalic.ttf
Normal file
BIN
public/fonts/Spectral-MediumItalic.ttf
Normal file
Binary file not shown.
BIN
public/fonts/Spectral-Regular.ttf
Normal file
BIN
public/fonts/Spectral-Regular.ttf
Normal file
Binary file not shown.
BIN
public/fonts/Spectral-SemiBold.ttf
Normal file
BIN
public/fonts/Spectral-SemiBold.ttf
Normal file
Binary file not shown.
BIN
public/fonts/Spectral-SemiBoldItalic.ttf
Normal file
BIN
public/fonts/Spectral-SemiBoldItalic.ttf
Normal file
Binary file not shown.
3922
public/images/logo.svg
Normal file
3922
public/images/logo.svg
Normal file
File diff suppressed because it is too large
Load Diff
|
After Width: | Height: | Size: 398 KiB |
13
server/DataCollection.js
Normal file
13
server/DataCollection.js
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
import {MongoInternals} from 'meteor/mongo';
|
||||||
|
import {Meteor} from 'meteor/meteor';
|
||||||
|
|
||||||
|
let uri = process.env.MONGO_URL2; //"mongodb://localhost:27017/avusd_data_collection";
|
||||||
|
//uri = "mongodb://localhost:27017/avusd_data_collection";
|
||||||
|
//console.log(uri);
|
||||||
|
let db2 = new MongoInternals.RemoteCollectionDriver(uri);
|
||||||
|
let collection = new Mongo.Collection("records", {_driver: db2});
|
||||||
|
Meteor.Records = collection;
|
||||||
|
|
||||||
|
|
||||||
|
// let results = collection.find({deviceId: "1e3e99ef-adf4-4aa2-8784-205bc60f0ce3"}).fetch();
|
||||||
|
// console.log(results);
|
||||||
90
server/logging.js
Normal file
90
server/logging.js
Normal file
@@ -0,0 +1,90 @@
|
|||||||
|
import FileRotateTransport from "winston-daily-rotate-file";
|
||||||
|
import winston from "winston";
|
||||||
|
import moment from "moment";
|
||||||
|
import _ from 'underscore';
|
||||||
|
|
||||||
|
let production = (process.env.NODE_ENV === "production");
|
||||||
|
let logPath = process.env.LOG_PATH;
|
||||||
|
|
||||||
|
let fileTransport = logPath ? new FileRotateTransport({
|
||||||
|
format: winston.format.combine(
|
||||||
|
winston.format.simple(),
|
||||||
|
winston.format.printf((info) => {
|
||||||
|
return moment(info.timestamp).format('YYYY-MM-DD hh:mm:ss SSSS') + " " + info.message;
|
||||||
|
})
|
||||||
|
),
|
||||||
|
auditFile: "audit.json",
|
||||||
|
maxSize: '1m',
|
||||||
|
maxFiles: 20,
|
||||||
|
dirname: logPath,
|
||||||
|
filename: 'DistrictCentral-%DATE%.log',
|
||||||
|
dateFormat: "YYYY-MM-DD"
|
||||||
|
}) : null;
|
||||||
|
let consoleColorTransport = new winston.transports.Console({
|
||||||
|
format: winston.format.combine(
|
||||||
|
winston.format.cli()
|
||||||
|
),
|
||||||
|
});
|
||||||
|
let consoleTransport = new winston.transports.Console({
|
||||||
|
format: winston.format.combine(
|
||||||
|
winston.format.simple()
|
||||||
|
),
|
||||||
|
});
|
||||||
|
let transports = production ? (logPath ? [fileTransport] : [consoleTransport]) : (logPath ? [fileTransport, consoleColorTransport] : [consoleColorTransport]);
|
||||||
|
//let transports = [fileTransport, consoleTransport];
|
||||||
|
|
||||||
|
//TODO: Use GrayLog or SysLog and interface with a log server.
|
||||||
|
|
||||||
|
/* Use GrayLog2 Transport
|
||||||
|
const Graylog2 = require('winston-graylog2');
|
||||||
|
logger.add(new Graylog2(options));
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* Use Syslog Transport
|
||||||
|
npm install winston-syslog
|
||||||
|
const winston = require('winston');
|
||||||
|
|
||||||
|
//
|
||||||
|
// Requiring `winston-syslog` will expose
|
||||||
|
// `winston.transports.Syslog`
|
||||||
|
//
|
||||||
|
require('winston-syslog').Syslog;
|
||||||
|
|
||||||
|
winston.add(new winston.transports.Syslog(options));
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Setup the logger.
|
||||||
|
let logger = winston.createLogger({
|
||||||
|
level: production ? 'info' : 'silly',
|
||||||
|
// format: winston.format.combine(
|
||||||
|
// winston.format.timestamp({format: 'YYYY-MM-DD hh:mm:ss SSSS'})
|
||||||
|
// ),
|
||||||
|
format: winston.format(function(info) {
|
||||||
|
//Add a timestamp to the info structure.
|
||||||
|
info.timestamp = new Date();
|
||||||
|
|
||||||
|
return info;
|
||||||
|
})(),
|
||||||
|
defaultMeta: {app: 'DistrictCentral'},
|
||||||
|
transports: transports
|
||||||
|
});
|
||||||
|
|
||||||
|
let consoleLogOriginal = console.log;
|
||||||
|
|
||||||
|
// Override the log and error functions.
|
||||||
|
console.log = function(d) {
|
||||||
|
// For some reason Meteor requires the original console.log for the initial listening log output. If that isn't performed then Meteor never starts properly.
|
||||||
|
if(arguments.length === 1 && arguments[0] === 'LISTENING') {
|
||||||
|
return consoleLogOriginal.call(console, 'LISTENING');
|
||||||
|
}
|
||||||
|
else logger.log("debug", _.isObject(d) ? JSON.stringify(d) : d);
|
||||||
|
}
|
||||||
|
console.info = function(d) {
|
||||||
|
logger.log("info", _.isObject(d) ? JSON.stringify(d) : d);
|
||||||
|
}
|
||||||
|
console.warn = function(d) {
|
||||||
|
logger.log("warn", _.isObject(d) ? JSON.stringify(d) : d);
|
||||||
|
}
|
||||||
|
console.error = function(e) {
|
||||||
|
logger.log("error", e.stack || e);
|
||||||
|
}
|
||||||
@@ -1,40 +1,35 @@
|
|||||||
|
|
||||||
|
import './DataCollection.js';
|
||||||
import '../imports/api/';
|
import '../imports/api/';
|
||||||
import './google-oauth.js';
|
import './google-oauth.js';
|
||||||
import '/imports/startup/accounts-config.js';
|
import '/imports/startup/accounts-config.js';
|
||||||
|
import './logging';
|
||||||
|
|
||||||
import url from 'url';
|
|
||||||
//import './google-oauth.js';
|
|
||||||
import connectRoute from 'connect-route';
|
|
||||||
|
|
||||||
/* Did not work at all.. not sure why.
|
|
||||||
let WebSocketServer = require("ws").Server;
|
|
||||||
//var wss = new WebSocketServer({ port: env.PORT });
|
|
||||||
let wss = new WebSocketServer({host: '192.168.3.101', port: 3001});
|
|
||||||
|
|
||||||
console.log("Starting WS");
|
|
||||||
|
|
||||||
wss.on("connection", function (ws) {
|
//Some basic testing of detecting which mode the app is running under...
|
||||||
console.log("WS Open");
|
|
||||||
|
|
||||||
ws.on("message", function(data) {
|
|
||||||
console.log(data);
|
|
||||||
ws.send("Pong");
|
|
||||||
}).on("error", (err) => {
|
|
||||||
console.log(err);
|
|
||||||
})
|
|
||||||
}).on('error', (err) => {
|
|
||||||
console.error(err);
|
|
||||||
});
|
|
||||||
|
|
||||||
// const net = require('net');
|
|
||||||
//
|
//
|
||||||
// const server = net.createServer((socket) => {
|
// if(process.env.NODE_ENV === "development") {
|
||||||
// socket.on('data', (data) => {
|
// console.log("In Dev Mode");
|
||||||
// console.log(data.toString());
|
// }
|
||||||
// socket.write("Pong");
|
// else if(process.env.NODE_ENV === "production") {
|
||||||
// });
|
// console.log("In Prod Mode");
|
||||||
// }).on('error', (err) => {
|
// }
|
||||||
// console.error(err);
|
// else {
|
||||||
// });
|
// console.log("No idea what mode we are in!");
|
||||||
|
// }
|
||||||
|
|
||||||
|
|
||||||
|
//TEST LOG OUTPUT...
|
||||||
|
// let obj = {test: 'abc'};
|
||||||
|
// console.log("Test Output");
|
||||||
|
// console.log(obj);
|
||||||
|
//
|
||||||
|
// try {
|
||||||
|
// throw Exception("Error!!");
|
||||||
|
// }
|
||||||
|
// catch(err) {
|
||||||
|
// console.error(err);
|
||||||
|
// }
|
||||||
|
|
||||||
*/
|
|
||||||
|
|||||||
Reference in New Issue
Block a user