From f25a63271d71bbd209e5bf1299bfe5238d1e8a25 Mon Sep 17 00:00:00 2001 From: Kenneth Jao Date: Mon, 7 Nov 2016 21:22:51 -0500 Subject: [PATCH 1/2] More menu items --- hourglass/client/main/main.html | 4 ++ hourglass/client/main/main.js | 2 +- hourglass/client/menus/menus.css | 93 ++++++++++++++++++++++++++++++- hourglass/client/menus/menus.html | 77 ++++++++++++++++++++++--- hourglass/client/menus/menus.js | 56 ++++++++++++++++++- 5 files changed, 221 insertions(+), 11 deletions(-) diff --git a/hourglass/client/main/main.html b/hourglass/client/main/main.html index 20961ee..c8df587 100644 --- a/hourglass/client/main/main.html +++ b/hourglass/client/main/main.html @@ -60,6 +60,10 @@ {{#if currSettingMode 'addClass'}} {{> joinClass}} {{/if}} + + {{$if currSettingMode 'createClass'}} + {{> createClass}} + {{/if}} diff --git a/hourglass/client/main/main.js b/hourglass/client/main/main.js index b8e88a5..5cacee8 100644 --- a/hourglass/client/main/main.js +++ b/hourglass/client/main/main.js @@ -41,7 +41,7 @@ Template.login.rendered = function() { Template.main.created = function() { Session.set("mode", Session.get("user").preferences.mode); - Session.set("classInfo", Session.get("user").classes[0]); + Session.set("classInfo", null); } Template.main.rendered = function() { diff --git a/hourglass/client/menus/menus.css b/hourglass/client/menus/menus.css index b83b278..f779aca 100644 --- a/hourglass/client/menus/menus.css +++ b/hourglass/client/menus/menus.css @@ -298,7 +298,7 @@ display: inline-block; } -#infoClassCont div .fa-files-o{ +#infoClassCont div .fa-files-o, #infoClassCont div .fa-pencil-square-o { font-size: 2vh; position: absolute; @@ -335,6 +335,41 @@ position: absolute; } +#changeAdminWrapper { + margin-top: 1%; + display: none; +} + +#changeAdminWrapper span { + font-size: 2vh; + display: inline-block !important; +} + +#changeAdmin { + width: 40%; + margin: 0 1% 0 1%; + font-size: 2vh; + padding: 1%; + outline: none; +} + +#adminSubmit { + padding: 2%; + background-color: rgba(0,0,0,0.1); + display: inline-block; + + cursor: pointer; + + -webkit-transition: background-color 0.4s ease; + -moz-transition: background-color 0.4s ease; + -ms-transition: background-color 0.4s ease; + transition: background-color 0.4s ease; +} + +#adminSubmit:hover { + background-color: rgba(0,0,0,0.2); +} + .userAdder { width: 100%; margin-bottom: 3%; @@ -400,3 +435,59 @@ .userDisp .fa:hover { color: #FF1A1A; } + +#joinTop { + margin: 2% 0 0 5%; +} + +#joinTop .-autocomplete-container { + display: none; +} + +#joinTop .fa { + font-size: 2.5vh; +} + +#classSearch, #privateCode { + font-size: 2vh; + padding: 1%; + outline: none; +} + +#private { + font-size: 2.4vh; + padding: 1.5%; + margin: 0 2% 0 2%; + + background-color: rgba(0,0,0,0.1); + + cursor: pointer; + + -webkit-transition: background-color 0.4s ease; + -moz-transition: background-color 0.4s ease; + -ms-transition: background-color 0.4s ease; + transition: background-color 0.4s ease; +} + +#private:hover { + background-color: rgba(0,0,0,0.2); +} + +#privateCode { + width: 25%; + display: none; + + -webkit-animation: expand .7s ease 1; + animation: expand .7s ease 1; +} + +@-webkit-keyframes expand { + 0% { width: 0%; } + 100% { width: 25%; } +} + +@keyframes expand { + 0% { width: 0%; } + 100% { width: 25%; } +} + diff --git a/hourglass/client/menus/menus.html b/hourglass/client/menus/menus.html index c2f34ee..b728960 100644 --- a/hourglass/client/menus/menus.html +++ b/hourglass/client/menus/menus.html @@ -187,10 +187,14 @@ Users
- {{#if classInfoMode 'general' 'a'}} - {{> classInfoGeneral}} + {{#if classSelected}} + {{#if classInfoMode 'general' 'a'}} + {{> classInfoGeneral}} + {{else}} + {{> classInfoUsers}} + {{/if}} {{else}} - {{> classInfoUsers}} +

Click on a class to see its info.

{{/if}}
@@ -213,7 +217,8 @@
{{> inputAutocomplete id="classSearch" settings=classSettings placeholder="Search..."}} - +

Join Private Class

+
@@ -254,10 +259,14 @@ Users
- {{#if classInfoMode 'general' 'a'}} - {{> classInfoGeneral}} + {{#if classSelected}} + {{#if classInfoMode 'general' 'a'}} + {{> classInfoGeneral}} + {{else}} + {{> classInfoUsers}} + {{/if}} {{else}} - {{> classInfoUsers}} +

Click on a class to see its info!

{{/if}}
@@ -265,6 +274,49 @@ + + + + + diff --git a/hourglass/client/menus/menus.js b/hourglass/client/menus/menus.js index b046f79..09020c2 100644 --- a/hourglass/client/menus/menus.js +++ b/hourglass/client/menus/menus.js @@ -1,5 +1,8 @@ Session.set("settingMode", "manageClass"); Session.set("classInfoMode", "general"); +Session.set("notsearching", true); // If user isn't searching +Session.set("noclass", null); // If user doesn't have classes. +Session.set("notfound", null); // If no results for autocomplete. var filterOpen = [false, true, true, true, true]; var sidebarMode = [null,null]; @@ -69,24 +72,14 @@ Template.sidebarMenuPlate.events({ }, // CLASS FILTERS 'click .sideClass' (event) { // Click class list in sidebar. - var div = event.target; - while (div.getAttribute("classid") === null) div = div.parentNode; - var classid = div.getAttribute("classid"); - - if (Session.equals("sidebarMode","create")) { // If creating work from calendar. - var newSetting = Session.get("currentWork"); - newSetting.class = classid; - Session.set("currentWork", newSetting); - openDivFade(document.getElementsByClassName("overlay")[0]); - } else { // Normal clicking turns on filter. - var array = Session.get("classDisp"); - if (array.indexOf(classid) !== -1) { - array.splice(array.indexOf(classid), 1); - } else { - array.push(classid); - } - Session.set("classDisp", array); + var classid = event.target.getAttribute("classid") + var array = Session.get("classDisp"); + if (array.indexOf(classid) !== -1) { + array.splice(array.indexOf(classid), 1); + } else { + array.push(classid); } + Session.set("classDisp", array); }, 'click .sideFilter' (event) { var div = event.target; @@ -180,6 +173,16 @@ Template.sidebarOptionPlate.events({ } }); +Template.sidebarCreatePlate.events({ + 'click .sideClass' (event) { // Click class list in sidebar. + var classid = event.target.getAttribute("classid"); + var newSetting = Session.get("currentWork"); + newSetting.class = classid; + Session.set("currentWork", newSetting); + $(".overlay").fadeIn(250); + } +}) + Template.registerHelper("classInfo", (info) => { var thisClass = classes.findOne({_id:Session.get("classInfo")}); var isYou = Session.equals("classInfo", Meteor.userId()); @@ -197,7 +200,8 @@ Template.registerHelper("classInfo", (info) => { case "admin": return Meteor.users.findOne({_id: (isYou) ? Meteor.userId() : thisClass.admin}); case "code": - return (isYou || !thisClass.code) ? {exists: false} : {exists: true, code: thisClass.code}; + if(isYou) return {exists: false} + return (isYou || Meteor.userId() !== this.admin) ? {exists: false} : {exists: true, code: Meteor.call('getCode', thisClass._id)}; case "mine": return (isYou) ? true : Meteor.userId() === thisClass.admin; case "moderators": @@ -275,8 +279,21 @@ Template.manageClass.events({ user._id, classid ]; - sendData("changeAdmin"); - input.value = ""; + Session.set("confirmText", "Change ownership?"); + confirm = "changeAdmin"; + $("#confirmOverlay").fadeIn(250); + }, + 'click #deleteClass' () { + serverData = Session.get("classInfo"); + confirm = "deleteClass"; + Session.set("confirmText", "Delete this class?"); + $("#confirmOverlay").fadeIn(250); + }, + 'click .classBox .fa-times' (event) { + serverData = event.target.parentNode.getAttribute("classid"); + confirm = "leaveClass"; + Session.set("confirmText", "Leave this class?"); + $("#confirmOverlay").fadeIn(250); } }); @@ -293,7 +310,9 @@ Template.joinClass.helpers({ ).fetch(); for (var i = 0; i < array.length; i++) { + array[i].join = true; array[i].subscribers = array[i].subscribers.length; + array[i].teachershort = array[i].teacher.split(" ").slice(1).reduce(function(a,b) { return a+ " " + b;}); } if (array.length === 0) { Session.set("noclass", true); @@ -385,6 +404,12 @@ Template.joinClass.events({ })); } catch (err) {} }, + 'click .classBox .fa-plus' (event) { + serverData = [event.target.parentNode.getAttribute("classid"), ""]; + confirm = "joinClass"; + Session.set("confirmText", "Join this class?"); + $("#confirmOverlay").fadeIn(250); + }, 'click #private' () { $("#privateCode").css('display','inline-block'); var input = document.getElementById("privateCode"); @@ -408,6 +433,80 @@ Template.joinClass.events({ } }); +Template.createClass.helpers({ + schoolComplete() { // Returns autocomplete array for schools. + return { + position: "bottom", + limit: 6, + rules: [{ + token: '', + collection: schools, + field: 'name', + matchAll: true, + template: Template.schoolList + }] + }; + }, + teacherComplete() { // Returns autocomplete array for teachers. + return { + position: "bottom", + limit: 1, + rules: [{ + token: '', + collection: teachers, + field: 'name', + template: Template.teacherList + }] + }; + }, +}); + +Template.createClass.events({ + 'click #creSubmit' () { + var inputs = document.getElementsByClassName("creInput"); + var values = new Object; + var required = ["school","name","privacy","category"]; + var no = []; + for(var i = 0; i < inputs.length; i++) { + var val = inputs[i].value; + var where = inputs[i].getAttribute("form"); + if(val === "" && _.contains(required, where)) { + no.push(where); + } + values[where] = val; + } + + if(no.length !== 0) { // Check missing fields. + sAlert.error("Missing " + no.reduce(function(a,b) { return (b === no[no.length-1]) ? a + ", and " + b : a + ", " + b }), { + effect: 'stackslide', + position: 'top', + timeout: 3000 + }); + return; + } + values.privacy = (values.privacy === "Public") ? false : true; + values.status = false; + values.category.toLowerCase(); + values.code = ""; + serverData = values; + if(!teachers.findOne({name: values["teacher"]})) { + Meteor.call("createTeacher", values["teacher"], function(error,result) { + console.log(error); + if (error !== undefined) { + sAlert.error(error.message, { + effect: 'stackslide', + position: 'top' + }); + } else { + sendData("createClass"); + } + }); + } else { + sendData("createClass"); + } + } +}); + Template.classInfoUsers.events({ 'click .userAdder .fa' (event) { var type = event.target.getAttribute("user"); diff --git a/hourglass/collections/main.js b/hourglass/collections/main.js index 7cfa5be..8aff872 100644 --- a/hourglass/collections/main.js +++ b/hourglass/collections/main.js @@ -1,13 +1,19 @@ schools = new Mongo.Collection("Schools"); +teachers = new Mongo.Collection("Teachers"); classes = new Mongo.Collection("Classes"); work = new Mongo.Collection("Work"); requests = new Mongo.Collection("Requests"); admins = Meteor.users; schools.schema = new SimpleSchema({ - name: {type: String}, + name: {type: String} }); +teachers.schema = new SimpleSchema({ + name: {type: String}, + school: {type: String} +}) + classes.schema = new SimpleSchema({ school: {type: String}, //icon: {type: String}, @@ -57,6 +63,7 @@ userSchema = new SimpleSchema({ }); schools.attachSchema(schools.schema); +teachers.attachSchema(teachers.schema); classes.attachSchema(classes.schema); work.attachSchema(work.schema); requests.attachSchema(requests.schema); diff --git a/hourglass/lib/adminConfig.js b/hourglass/lib/adminConfig.js index bef4086..82fb376 100644 --- a/hourglass/lib/adminConfig.js +++ b/hourglass/lib/adminConfig.js @@ -10,6 +10,16 @@ AdminConfig = { color: 'red', label: 'Schools' }, + teachers: { + icon: 'book', + tableColumns: [ + { label: 'ID', name: '_id' }, + { label: 'Name', name: 'name' }, + { label: 'School', name: 'school'} + ], + color: 'orange', + label: 'Teachers' + }, classes: { icon: 'graduation-cap', tableColumns: [ diff --git a/hourglass/lib/constants.js b/hourglass/lib/constants.js index c21d2ac..03bc54a 100644 --- a/hourglass/lib/constants.js +++ b/hourglass/lib/constants.js @@ -1,9 +1,9 @@ themeColors = { "lux": { "background": "White.jpg", - "mainColor": "#EEE", - "secondaryColor": "#FEFEFE", - "sidebarColor": "#799cb8", + "mainColor": "#DBDBDB", + "secondaryColor": "#E8E8E8", + "sidebarColor": "#799CB8", "userDropdownColor": "#E6E6E6", "iconHighlight": "#FFF", "modeHighlight": "#D34949", @@ -28,8 +28,8 @@ themeColors = { "sidebarColor": "#327C5A", "userDropdownColor": "#CC3333", "iconHighlight": "#327C5A", - "modeHighlight": "#C9fE62", - "classCardColor":"#302c36", + "modeHighlight": "#C9FE62", + "classCardColor":"#302C36", "textColor": "#FCF0F0" }, "aequor": { @@ -50,7 +50,7 @@ themeColors = { "sidebarColor": "#6D9957", "userDropdownColor": "#89BB52", "iconHighlight": "#91EE61", - "modeHighlight": "#B9f643", + "modeHighlight": "#B9F643", "classCardColor":"#C18311", "textColor": "#FCF0F0" }, @@ -121,4 +121,5 @@ options = { ] } -serverData = null; \ No newline at end of file +serverData = null; +confirm = null; \ No newline at end of file diff --git a/hourglass/lib/router.js b/hourglass/lib/router.js index 6f7167e..50de376 100644 --- a/hourglass/lib/router.js +++ b/hourglass/lib/router.js @@ -6,6 +6,7 @@ Router.route('/', { return [ Meteor.subscribe('classes', this.params._id), Meteor.subscribe('schools', this.params._id), + Meteor.subscribe('teachers', this.params._id), Meteor.subscribe('work', this.params._id), Meteor.subscribe('requests', this.params._id), Meteor.subscribe('users', this.params._id) @@ -45,6 +46,7 @@ Router.route('/profile', { return [ Meteor.subscribe('classes', this.params._id), Meteor.subscribe('schools', this.params._id), + Meteor.subscribe('teachers', this.params._id), Meteor.subscribe('work', this.params._id), Meteor.subscribe('requests', this.params._id), Meteor.subscribe('users', this.params._id) @@ -57,30 +59,6 @@ Router.route('/profile', { } }); -Router.route('/user/:email', { - data: function() { - return Meteor.users.findOne({'services.google.email': this.params.email}); - }, - waitOn: function() { - return [ - Meteor.subscribe('users', this.params._id) - ]; - }, - action: function() { - if(Meteor.users.findOne({'services.google.email': this.params.email}) !== undefined) { - if(Meteor.user() && this.params.email === Meteor.user().services.google.email) { - Session.set("user", Meteor.user().profile); - this.redirect('/profile'); - } else { - Session.set("user", Meteor.user().profile); - this.render('user'); - } - } else { - this.render("NotFound"); - } - } -}); - Router.configure({ notFoundTemplate: "NotFound" }); diff --git a/hourglass/server/main.js b/hourglass/server/main.js index db1ec44..31b4e8c 100644 --- a/hourglass/server/main.js +++ b/hourglass/server/main.js @@ -25,6 +25,10 @@ Meteor.publish('schools', function() { return schools.find(); }); +Meteor.publish('teachers', function() { + return teachers.find(); +}) + // Returns the code for classes (for debug) Meteor.publish('classes', function() { @@ -151,7 +155,9 @@ var errors = [ ["unauthorized", "This class has not been approved yet"], ["unauthorized", "Sorry, you are not authorized to complete this action."], - ["other", "Error could not be processed"] + ["other", "Error could not be processed"], + ["matching", "This teacher already exists!"], + ["trivial", "Please put the full name!"] ]; function securityCheck(checklist, input) { @@ -182,10 +188,9 @@ function securityCheck(checklist, input) { case 3: if (!schools.findOne({name: input.school})) error = 2; break; - // TODO: teachers with same name // Duplicate classes case 4: - if (classes.findOne({school: input.school, status: true, privacy: false, teacher: input.teacher, hour: input.hour}) || (input.teacher === "" && input.hour === "")) error = 3; + if (classes.findOne({school: input.school, teacher: input.teacher, status: true, privacy: false, hour: input.hour}) || (input.teacher === "" && input.hour === "")) error = 3; break; // Class admin case 5: @@ -266,6 +271,14 @@ function securityCheck(checklist, input) { case 25: if (Meteor.userId() === null) error = errors.length - 1; break; + // Teacher already exists + case 26: + if (teachers.findOne({name: {$eq: input.teacher}, school: {$eq: input.school}})) error = 21; + break; + // Incorrect teacher format + case 27: + if(input.split(" ").length < 2) error = 22; + break; } results.push(error); } @@ -317,7 +330,18 @@ Meteor.methods({ throw new Meteor.Error(errors[security]); } }, - + 'createTeacher': function(name) { + teachers.schema.validate({name: name}); + var security = securityCheck([26, 27, true], + name); + if(!security) { + teachers.insert({ + name: name + }); + } else { + throw new Meteor.Error(errors[security]); + } + }, // Class Functions 'createClass': function(input) { classes.schema.validate(input); @@ -335,7 +359,6 @@ Meteor.methods({ input.subscribers = []; input.moderators = []; input.banned = []; - classes.insert(input, function(err, result) { Meteor.call('joinClass', [result, input.code]); }); @@ -711,9 +734,11 @@ Meteor.methods({ var current = Meteor.user().profile; var index = current.classes.indexOf(change); if (index >= 0) { + console.log("hi"); if (classes.findOne({ _id: change }).admin != Meteor.userId()) { + console.log("f"); current.classes.splice(index, 1); Meteor.users.update({ _id: Meteor.userId()