Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
54 changes: 51 additions & 3 deletions client/css/style.css
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,6 @@ footer ul {
list-style: none;
padding-left: 0;
}
body, html {
height: 100%;
}
body {
font-family: 'Open Sans', sans-serif;
margin-top: 50px;
Expand Down Expand Up @@ -185,3 +182,54 @@ div .txt-help {
background: #EFEFEF;
padding: 30px;
}

/* Instructors */
.instructor .main, .instructors .main,
.course .main, .courses .main {
background: #555;
max-width: 700px;
margin: auto;
color: #fff;
padding-bottom: 1em;
border-radius: 10px;
}
.instructor h1, .instructors h1,
.course h1, .courses h1 {
text-align: center;
margin: 5px 0 1em;
}
.instructor p, .instructors p,
.course p, .course p {
font-size: 1.2em;
}
.instructor #single, .instructors #single,
.course #single, .courses #single {
padding-bottom: 2em;
}
.instructor .content, .instructors .content,
.course .content, .courses .content {
padding: 2em 4em 1em;
}
#result, #added {
padding: 1em 0 0;
}
.success {
color: #8DC63F;
}
button.btn {
margin: 0 5px 10px 0;
}
input, select {
margin: 0 0 1em 0;
color: #000;
}
option {
padding: 8px 10px;
}
#collections ul {
padding: 1em 0 0;
}
#collections li {
list-style: none;
padding: 0 0 1em;
}
71 changes: 71 additions & 0 deletions client/spa/js/course/course.controller.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
'use strict';

var Backbone = require('../vendor/index').Backbone;
var $ = require('../vendor/index').$;
var Model = require('./course.model');
var View = require('./course.view');

module.exports = Backbone.Controller.extend({
routes: {
'courses/:id': 'showCourse',
'courses/new': 'addCourse'
},
initialize: function(){
this.options.container = this.options.container || 'body';
this.model = new Model();
this.view = new View({model: this.model});
},
showCourse: function(courseId, cb){
this.fetchModel(courseId, function(err){
var view;

this.view.remove();
this.view = new View({model: this.model});

if (err){
view = this.renderError();
} else {
this.view.template = this.view.showTemplate;
view = this.renderView();
}
if (cb){
cb(err, view);
}

}.bind(this));
},
addCourse: function() {
this.model = new Model();
this.model.isNew();

this.view.remove();

this.view.template = this.view.editTemplate;
this.renderView();
},
fetchModel: function(courseId, cb){
this.model.set({id: courseId});
this.model.fetch({
success: function(model, response, options){
//console.log(model);
cb(null, model);
},
error: function(model, response, options){
//console.error(response);
cb(response, model);
}
});
},
renderToContainer: function(view){
return $(this.options.container).html(view);
},
renderView: function(){
this.renderToContainer(this.view.render().$el);
this.view.delegateEvents(); // delegate for add in collections
return this.view;
},
renderError: function(){
return this.renderToContainer(
'<p>There was a problem rendering this course</p>');
}
});
10 changes: 10 additions & 0 deletions client/spa/js/course/course.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<div class="container main" id="single">
<div class="content">
<h1><%- title %></h1>
<p><strong>Course Type:</strong> <%- courseType %></p>
<h3>Description:</h3><p><%- description %></p>
<button class="modify btn btn-primary">Edit</button>
<button class="delete btn btn-danger">Delete</button>
<div id="result"></div>
</div>
</div>
29 changes: 29 additions & 0 deletions client/spa/js/course/course.model.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
'use strict';

var Backbone = require('../vendor/index').Backbone;
module.exports = Backbone.Model.extend({
defaults: {
title: '',
courseType: 'video',
description: ''
},
urlRoot: '/api/courses',
initialize: function(){
this.on('change', function(){

});
},
validate: function(attrs){
var errors = [];
if (!attrs.title){
errors.push('title cannot be empty');
}
if (!attrs.courseType){
errors.push('courseType cannot be empty');
}
if (!attrs.description){
errors.push('description cannot be empty');
}
return errors.length > 0 ? errors: false;
}
});
94 changes: 94 additions & 0 deletions client/spa/js/course/course.view.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
'use strict';
var Backbone = require('../vendor/index').Backbone;
var _ = require('../vendor/index')._;
var $ = require('../vendor/index').$;
var fs = require('fs'); //will be replaced by brfs in the browser
// readFileSync will be evaluated statically so errors can't be caught
var template = fs.readFileSync(__dirname + '/course.html', 'utf8');
var editTemplate = fs.readFileSync(__dirname + '/editCourse.html', 'utf8');

module.exports = Backbone.View.extend({
className: 'course',
template: _.template(template),
showTemplate: _.template(template),
editTemplate: _.template(editTemplate),
events: {
'click .delete': 'destroy',
'click .modify': 'modify',
'click .save': 'save',
'click .cancel': 'cancel'
},
initialize: function(){
this.listenTo(this.model, 'destroy', this.remove);
this.listenTo(this.model, 'change', this.render);
},
render: function(){
var context = this.model.toJSON();
this.$el.html(this.template(context));

// if it's adding new model, change button to Add
if (this.model.get('id') === undefined) {
this.$('.save').html('Add');
}

return this;
},
destroy: function(){
this.model.destroy();

$('body').append($('<div/>').addClass('course')
.append($('<div/>')
.addClass('container main')
.append($('<div/>')
.attr('id', 'result')
.addClass('success content')
.html('Successfully deleted course'))));

this.remove();
},
modify: function(e){
var context = this.model.toJSON();
this.$el.html(this.editTemplate(context));

return this;
},
save: function(e) {
// if there's no changes, do not do anything
e.preventDefault();

var formData = {
title: this.$('#title').val().trim(),
courseType: this.$('#courseType').val(),
description: this.$('#description').val().trim()
};

var check = {
success: function() {
$('#result').addClass('success')
.html('Successfully updated course')
.fadeIn().delay(4000).fadeOut();

var addNew = $('.save').html();

if (addNew === 'Add') {
$('#added').addClass('success')
.html('Successfully added new course')
.fadeIn().delay(4000).fadeOut();
}
},
error: function(model, errors) {
_.each(errors, function (err) {
$('#result').addClass('error');

$('input').find('.help-inline').text(err.message);
}, this);
}
};

this.model.save(formData, check);
},
cancel: function(e) {
e.preventDefault(); // prevent event bubbling
this.render();
}
});
51 changes: 51 additions & 0 deletions client/spa/js/course/courses.collection.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
'use strict';

var Backbone = require('../vendor/index').Backbone;

module.exports = Backbone.Collection.extend({
url: '/api/courses/',

initialize: function(){
this.on('sortById', this.sortById);
this.on('sortByTitle', this.sortByTitle);
this.on('sortByCourseType', this.sortByCourseType);
this.on('addNew', this.addNew);
this.on('filterByCourseType', this.filterByCourseType);
this.trigger('sortById');
},

sortById: function(){
this.comparator = function(model){
return model.get('id');
};
this.sort();
},

sortByTitle: function(){
this.comparator = function(model){
return model.get('title');
};
this.sort();
},

sortByCourseType: function(){
this.comparator = function(model){
return model.get('courseType');
};
this.sort();
},

addNew: function() {
this.create = function(model) {
model.get('title');
model.get('courseType');
model.get('description');
};
},

filterByCourseType: function() {
var filtered = this.where({courseType: 'instructor led'});

return new Backbone.Collection(filtered);
}
});
66 changes: 66 additions & 0 deletions client/spa/js/course/courses.controller.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
'use strict';

var Backbone = require('../vendor/index').Backbone;
var $ = require('../vendor/index').$;
var Model = require('./course.model');
var Collection = require('./courses.collection');
var View = require('./courses.view');

module.exports = Backbone.Controller.extend({
routes: {
'courses': 'showCourses'
},
initialize: function(){
this.options.container = this.options.container || 'body';

// listen to event from instructor controller
this.on('display:courses', function(data) {
// make sure this view exist
this.getView();
this.view.trigger('display:courses', data);
});
},
getCollection: function(){
if (!this.collection){
Collection = Collection.extend({model: Model});
this.collection = new Collection();
}
return this.collection;
},
getView: function(){
if (!this.view){
var V = View.extend({collection: this.collection});
this.view = new V();
this.view.on('addNew', function() {
// trigger the router for addNew
this.navigate('courses/new', { trigger: true });
}.bind(this));
}
return this.view;
},
showCourses: function(){
var self = this;

this.getCollection().fetch({
success: function(collection, response, options){
self.getView();
self.renderView();
},
error: function(collection, response, options){
self.renderError();
}
});
},
renderToContainer: function(html){
return $(this.options.container).html(html);
},
renderView: function(){
this.renderToContainer(this.view.render().$el);
this.view.delegateEvents();
return this.view;
},
renderError: function(){
return this.renderToContainer(
'<p>There was a problem rendering courses</p>');
}
});
Loading