Converted sample project into an almost working Svelte / Meteor project. Needs to fix the login/logout code, the camera code, and add Chromebook history when the QR Code is scanned.

This commit is contained in:
2022-04-02 10:29:35 -07:00
parent 9e0e231152
commit 038c68f618
34 changed files with 3997 additions and 101 deletions

View File

@@ -0,0 +1,52 @@
import { Meteor } from 'meteor/meteor';
import { Mongo } from 'meteor/mongo';
import { check } from 'meteor/check';
import { MongoClient } from 'mongodb';
//export const Records = new Mongo.Collection('records');
let client;
let database;
let dataCollection;
if (Meteor.isServer) {
let uri = process.env.MONGO_URL2;
client = new MongoClient(uri);
database = client.db("avusd-data-collection");
dataCollection = database.collection("records");
// This code only runs on the server
Meteor.publish('chromebookData', function(deviceId) {
check(deviceId, String);
return dataCollection.find({deviceId});
});
}
Meteor.methods({
// '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 } });
// },
});

View File

@@ -2,9 +2,21 @@ import { Meteor } from 'meteor/meteor';
if (Meteor.isServer) {
Meteor.methods({
'users.login'() {
console.log("Login Called.");
}
});
}
Meteor.publish(null, function() {
if(this.userId) {
return Meteor.roleAssignment.find({'user._id': this.userId});
}
else {
this.ready();
}
})
// Meteor.methods({
// 'users.setupInitialRoles'() {
// Roles.createRole('admin');
// Roles.createRole('laptop-management');
// Roles.addRolesToParent('laptop-management', 'admin');
// Roles.addUsersToRoles("zwbMiaSKHix4bWQ8d", 'admin', 'global');
// }
// });
}

View File

@@ -1,8 +1,10 @@
import { Accounts } from 'meteor/accounts-base'
Accounts.ui.config({
passwordSignupFields: 'USERNAME_ONLY'
});
if(Meteor.isCLient) {
Accounts.ui.config({
passwordSignupFields: 'USERNAME_ONLY'
});
}
Accounts.config({
restrictCreationByEmailDomain: function(address) {

5
imports/ui/App.css Normal file
View File

@@ -0,0 +1,5 @@
nav {
font-size: 1.4rem;
}
/*# sourceMappingURL=App.css.map */

1
imports/ui/App.css.map Normal file
View File

@@ -0,0 +1 @@
{"version":3,"sourceRoot":"","sources":["App.sass"],"names":[],"mappings":"AAAA;EACC","file":"App.css"}

2
imports/ui/App.sass Normal file
View File

@@ -0,0 +1,2 @@
nav
font-size: 1.4rem

View File

@@ -1,12 +1,159 @@
<style>
nav {
font-size: 2rem;
}
a {
display: block;
color: green;
text-decoration: none;
}
html {
background-color: #000121;
font-family: 'Roboto', sans-serif;
}
/** Forbidden CSS */
.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>
<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";
let currentUser;
//import './imports/ui/App.css';
//let currentUser;
onMount(async () => {
// Meteor.subscribe('records');
@@ -15,6 +162,18 @@
// $: 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}});
// $: {
@@ -45,43 +204,66 @@
function performLogout() {
Meteor.logout();
}
</script>
<div class="container">
<header>
<!-- <h1>Todo List ({ $incompleteCount })</h1>-->
<!-- <label className="hide-completed">-->
<!-- <input-->
<!-- type="checkbox"-->
<!-- bind:checked={hideCompleted}-->
<!-- />-->
<!-- Hide Completed Tasks-->
<!-- </label>-->
<!-- <BlazeTemplate template="loginButtons"/>-->
{#if !$currentUser}
<button type="button" on:click={performLogin}>Login</button>
{:else}
<button type="button" on:click={performLogout}>Logout</button>
{/if}
<!--{#if $currentUser}-->
<!-- <form class="new-task" on:submit|preventDefault={handleSubmit}>-->
<!-- <input-->
<!-- type="text"-->
<!-- placeholder="Type to add new tasks"-->
<!-- bind:value={newTask}-->
<!-- />-->
<!-- </form>-->
<!--{/if}-->
</header>
<ul>
<!--{#each tasks as task}-->
<!-- <Task-->
<!-- key={task._id}-->
<!-- task={task}-->
<!-- />-->
<!--{/each}-->
</ul>
</div>
<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}

View File

@@ -0,0 +1,41 @@
<style>
.error {
color: darkred;
}
</style>
<script>
import {Html5QrcodeScanner} from "html5-qrcode";
function onScanSuccess(decodedText, decodedResult) {
console.log('Code matched ' + decodedResult);
document.getElementById("log").prepend(decodedText);
}
function onScanFailure(error) {
}
function scanner() {
let html5QrcodeScanner = new Html5QrcodeScanner("reader", {
fps: 10,
qrbox: {width: 250, height: 250}
}, /* verbose */ false);
html5QrcodeScanner.render(onScanSuccess, onScanFailure);
}
</script>
<div className="container">
<div class="row">
Hello World!
</div>
<div class="row">
<div use:scanner id="reader" width="300px"></div>
</div>
<div class="row">
<div id="log" class="col-12">
</div>
</div>
</div>

View File

@@ -0,0 +1,34 @@
<style>
nav {
font-size: 2rem;
}
a {
display: block;
color: green;
text-decoration: none;
}
</style>
<script>
import {Route} from 'tinro';
import ChromebookScan from './ChromebookScan.svelte';
</script>
<Route path="/">
<div className="container">
<div class="row">
<nav class="col-12 center">
<h1>Chromebook Management</h1>
<a href="chromebooks/scan">Scan A Chromebook</a>
<a href="chromebooks/byStudent">Chromebook History By Student</a>
<a href="chromebooks/byChromebook">Chromebook History By Chromebook</a>
</nav>
</div>
</div>
</Route>
<Route path="/scan">
<ChromebookScan/>
</Route>
<Route path="/byStudent">TODO: Use student email to look up Chromebook history</Route>
<Route path="/byChromebook">TODO: Use chromebook ID (or picture of ID) to look up Chromebook history</Route>

View File

@@ -0,0 +1,12 @@
<script>
import {Meteor} from "meteor/meteor";
import {onMount} from 'svelte';
onMount(async () => {
// Meteor.subscribe('records');
});
</script>
<div class="container">
<h1>Process Laptops</h1>
</div>

2
imports/util/JsBarcode.min.js vendored Normal file

File diff suppressed because one or more lines are too long

2363
imports/util/qrious.js Normal file

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

6
imports/util/qrious.min.js vendored Normal file

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long