diff --git a/dist/index.html b/dist/index.html index 559b18ecd..a25ff93eb 100644 --- a/dist/index.html +++ b/dist/index.html @@ -3,13 +3,116 @@ Backbone Baseline + + +
+
+

Video Store Plus

+
+
+
- +
+
+

Search for Movies to Add

+
+
+
+ + + +
+
+

Movies that Match your Search

+
+ + + + + + + + + + + + +
MovieRelease DateOverviewAdd
+
+
+ +
+
+

Available Movies for Checkout

+
+ +
+
+ + + + + + + + + + + +
MovieRelease DateOverview
+
+ + + + + + + + - + diff --git a/package-lock.json b/package-lock.json index 2d3371d51..0962796f4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -3045,9 +3045,9 @@ "dev": true }, "foundation-sites": { - "version": "6.4.4-rc1", - "resolved": "https://registry.npmjs.org/foundation-sites/-/foundation-sites-6.4.4-rc1.tgz", - "integrity": "sha512-26cL66QFNqMVwM7bmIEqq4jiW+6CkIeW719ci1pchdJ4UK0Om+3Jl7MhkX/lzdzRHB75f2m1IK9lxk3JGOwApA==", + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/foundation-sites/-/foundation-sites-6.3.1.tgz", + "integrity": "sha1-I4Uzct65SAwTtCZJnq8Mj5DNvh0=", "requires": { "jquery": "3.2.1", "what-input": "4.3.1" diff --git a/package.json b/package.json index 97144b128..e5071c617 100644 --- a/package.json +++ b/package.json @@ -57,7 +57,7 @@ }, "dependencies": { "backbone": "^1.3.3", - "foundation-sites": "^6.4.4-rc1", + "foundation-sites": "^6.3.1", "jquery": "^3.2.1", "underscore": "^1.8.3" } diff --git a/spec/models/movie_spec.js b/spec/models/movie_spec.js new file mode 100644 index 000000000..a10a5fb99 --- /dev/null +++ b/spec/models/movie_spec.js @@ -0,0 +1,33 @@ +import Movie from 'models/movie'; + +describe('Quote spec', () => { + let movie; + beforeEach(() => { + movie = new movie({ + title: 'Test Movie', + release_date: '1998-10-10', + overview: 'some overview text here', + image_url: 'https://image.tmdb.org/t/p/w185/eruhq6kmjV7wopA7GjNDHrmAl89.jpg' + }); + }); + + describe('Buy function', () => { + it('increases the price by $1.00', () => { + const startPrice = quote.get('price'); + + quote.buy(); + + expect(quote.get('price')).toEqual(startPrice + 1.00); + }); + }); + + describe('Sell function', () => { + it('decreases the price by $1.00', () => { + const startPrice = quote.get('price'); + + quote.sell(); + + expect(quote.get('price')).toEqual(startPrice - 1.00); + }); + }); +}); diff --git a/src/app.js b/src/app.js index 30c00d594..c1d8ee279 100644 --- a/src/app.js +++ b/src/app.js @@ -6,9 +6,71 @@ import './css/styles.css'; import $ from 'jquery'; import _ from 'underscore'; -// ready to go +// Models and Collection +import Movie from 'models/movie'; +import ReturnedMovie from 'models/returned_movie'; +import MovieList from 'collections/movie_list'; +import ReturnedMovieList from 'collections/returned_movie_list'; + +// Views +import MovieView from 'views/movie_view'; +import MovieListView from 'views/movie_list_view'; +import ReturnedMovieView from 'views/returned_movie_view'; // TODO: Do I need this? +import ReturnedMovieListView from 'views/returned_movie_list_view'; + +const movieList = new MovieList(); +const returnedList = new ReturnedMovieList(); + +let movieTemplate; +let returnedMovieTemplate; + + $(document).ready(function() { + let bus = {}; + bus = _.extend(bus, Backbone.Events); + + movieTemplate = _.template($('#movie-template').html()); + returnedMovieTemplate = _.template($('#returned-movie-template').html()); + + $('#imdb-section, #search-btn').hide(); + + // Fetches all movies currently in the rental store + movieList.fetch({ + success: (model, response) => { + response.forEach((movie) => { + movieList.add(movie); + }); + }, + error: (model, response) => { + console.log(`This is the model: ${model} in the app.js`); + console.log(`This is the response: ${response} in the app.js`); + }, + }) + + const movieListView = new MovieListView({ + el: '#current-rentals-view', + template: movieTemplate, + model: movieList, + bus: bus, + }); - $('#main-content').append('

Hello World!

'); + const returnedMovieListView = new ReturnedMovieListView({ + el: '#returned-movies-view', + template: returnedMovieTemplate, + model: returnedList, + bus: bus, + }); -}); + // TODO: Event if the button to search is not showing, it is still possible to + // Search for a movie if you press center + // Need to figure out how to prevent this from happening + $('input[type=radio][name=searchLocation]').change(function(){ + if (this.value === 'imdb'){ + $('#imdb-section, #search-btn').show(); + $('#current-rentals-view').hide(); + } else { + $('#imdb-section, #search-btn').hide(); + $('#current-rentals-view').show(); + } + }); +}); // DOCUMENT READY diff --git a/src/collections/movie_list.js b/src/collections/movie_list.js new file mode 100644 index 000000000..e8e3d6fbc --- /dev/null +++ b/src/collections/movie_list.js @@ -0,0 +1,12 @@ +import Backbone from 'backbone'; +import Movie from '../models/movie'; + +const MovieList = Backbone.Collection.extend({ + model: Movie, + url: 'http://localhost:3000/movies', + parse: function(response){ + return response['responseJSON']; + } +}); + +export default MovieList; diff --git a/src/collections/returned_movie_list.js b/src/collections/returned_movie_list.js new file mode 100644 index 000000000..afda3eb7a --- /dev/null +++ b/src/collections/returned_movie_list.js @@ -0,0 +1,9 @@ +import Backbone from 'backbone'; +import ReturnedMovie from '../models/returned_movie'; + +const returnedMovieList = Backbone.Collection.extend({ + model: ReturnedMovie, + url: 'http://localhost:3000/movies/?query=', +}); + +export default returnedMovieList; diff --git a/src/css/styles.css b/src/css/styles.css index 68a79a569..9c2783cd3 100644 --- a/src/css/styles.css +++ b/src/css/styles.css @@ -1,6 +1,69 @@ @include foundation-everything; +@import url('https://fonts.googleapis.com/css?family=Secular+One'); +@import url('https://fonts.googleapis.com/css?family=Open+Sans'); -main { +/*********** ALL STYLES ***********/ + +h1, h2, h3, h4 { + font-family: 'Secular One', sans-serif; +} + +body { + font-family: 'Open Sans', sans-serif; + font-size: 12px; +} + +.align-center { + text-align: center; +} + +.aqua-bg { + background-color: #ABEBC6; +} + +.button { + background-color: #ABEBC6; + color: #232f3e; + border-radius: 5px; +} + +td .overflow-auto { + overflow: scroll; + height: 220px; +} +/*********** HEADER ***********/ + +.main-header { + text-align: center; + background-color: #232f3e; + color: #E8E8E8; + padding: 10px; +} + +.main-h1-wrapper { + border: 2px #ABEBC6 solid; +} + +.main-h1-wrapper h1 { + margin: 0; +} + +/******* MAIN SECTION HEADERS *******/ + +.section-header { + border: 1px #B8B8B8 solid; + border-radius: 5px; + margin: 20px 0 20px 0; + +} + +.section-header h4 { + padding: 10px; + margin: 0; +} + + +/* main { background: lightblue; } @@ -36,7 +99,7 @@ aside label { div { display: inline; -} +} */ /* * { border-style: solid; diff --git a/src/models/movie.js b/src/models/movie.js new file mode 100644 index 000000000..f02a63b7b --- /dev/null +++ b/src/models/movie.js @@ -0,0 +1,39 @@ +import Backbone from 'backbone'; + +const Movie = Backbone.Model.extend({ + initialize(attributes){ + this.title = attributes.title, + this.overview = attributes.overview, + this.release_date = attributes.release_date, + this.image_url = attributes.image_url, + this.bus = attributes.bus, + this.url = 'http://localhost:3000/movies' + }, + validate(attributes){ + const errors = {} + + if (!attributes.title) { + errors['title'] = 'The title can not be blank'; + } + + if (!attributes.overview) { + errors['overview'] = 'The overview can not be blank'; + } + + if (!attributes.release_date) { + errors['release_date'] = 'The release date can not be blank'; + } + + if (!attributes.image_url) { + errors['image_url'] = 'The image url can not be blank'; + } + + if ( Object.keys(errors).length > 0 ) { + return errors; + } else { + return false; + } + }, +}); + +export default Movie; diff --git a/src/models/returned_movie.js b/src/models/returned_movie.js new file mode 100644 index 000000000..29f2a643f --- /dev/null +++ b/src/models/returned_movie.js @@ -0,0 +1,40 @@ +import Backbone from 'backbone'; + +const returnedMovie = Backbone.Model.extend({ + urlRoot: 'http://localhost:3000/movies', + + initalize(attributes){ + this.bus = attributes.bus; + this.title = attributes.title, + this.overview = attributes.overview, + this.release_date = attributes.release_date, + this.image_url = attributes.image_url + }, + validate(attributes){ + const errors = {} + + if (!attributes.title){ + errors['title'] = 'The title can not be blank'; + } + + if (!attributes.overview){ + errors['title'] = 'The overview can not be blank'; + } + + if (!attributes.release_date){ + errors['title'] = 'The release date can not be blank'; + } + + if (!attributes.image_url){ + errors['title'] = 'The image url can not be blank'; + } + + if ( Object.keys(errors).length > 0 ) { + return errors; + } else { + return false; + } + }, +}); + +export default returnedMovie; diff --git a/src/views/movie_list_view.js b/src/views/movie_list_view.js new file mode 100644 index 000000000..05274f03c --- /dev/null +++ b/src/views/movie_list_view.js @@ -0,0 +1,56 @@ +import Backbone from 'backbone'; +import Movie from '../models/movie'; +import MovieList from '../collections/movie_list'; +import MovieView from '../views/movie_view'; +import ReturnedMovieView from '../views/returned_movie_view'; +import $ from 'jquery'; + +const MovieListView = Backbone.View.extend({ + initialize(params) { + this.template = params.template; + this.bus = params.bus; + this.listenTo(this.model, 'update', this.render); + this.listenTo(this.bus, 'addMovie', this.addToCollection); + + // this.listenTo(this.bus, 'addToCollection', this.addToCollection); + }, + render() { + this.$('#movie-list').empty(); + + const lastMovie = this.model.at(this.model.length -1); + const movieView = new MovieView({ + tagName: 'tr', + template: this.template, + model: lastMovie, + bus: this.bus, + }); + + this.$('#movies-in-store').append(movieView.render().$el); + return this; + }, + addToCollection(attributes) { + const newMovie = new Movie(attributes); + if (newMovie.isValid()) { + const movieView = new MovieView({ + tagName: 'tr', + template: this.template, + model: newMovie, + bus: this.bus + }); + this.$('#movies-in-store').prepend(movieView.render().$el); + } else { + this.displayOrderErrors(this.validationError); + } + }, + displayOrderErrors(errors) { + const $errorDisplay = this.$('#available-movies-errors'); + $errorDisplay.empty(); + + Object.keys(errors).forEach((key) => { + $errorDisplay.append(`

${errors[key]}

`); + }); + }, +}); // MovieListView + + +export default MovieListView; diff --git a/src/views/movie_view.js b/src/views/movie_view.js new file mode 100644 index 000000000..3fa99fbef --- /dev/null +++ b/src/views/movie_view.js @@ -0,0 +1,16 @@ +import Backbone from 'backbone'; +import Movie from '../models/movie'; + +const MovieView = Backbone.View.extend({ + initialize(params) { + this.template = params.template; + this.bus = params.bus; + }, + render() { + const compiledTemplate = this.template(this.model.toJSON()); + this.$el.html(compiledTemplate); + return this; + }, +}); + +export default MovieView; diff --git a/src/views/returned_movie_list_view.js b/src/views/returned_movie_list_view.js new file mode 100644 index 000000000..d7fc0a45c --- /dev/null +++ b/src/views/returned_movie_list_view.js @@ -0,0 +1,75 @@ +import Backbone from 'backbone'; +import ReturnedMovie from '../models/returned_movie'; +import ReturnedMovieView from '../views/returned_movie_view'; +import Movie from '../models/movie'; +import $ from 'jquery'; + +const ReturnedMovieListView = Backbone.View.extend({ + initialize(params) { + this.template = params.template; + this.bus = params.bus; + this.listenTo(this.bus, 'addMovie', this.addMovie); + }, + events: { + 'click form #search-btn': 'matchingMovies' + }, + addMovie(movie){ + const newMovie = new ReturnedMovie(movie); + if (newMovie.isValid()) { + newMovie.save({}, { + success: (model, response) => { + this.$('#success-movie-add').append(`

Added ${newMovie.get('title')} to Your Rentals!

`); + this.bus.trigger('addToCollection', newMovie); + }, + error: (model, response) => { + this.$('#returned-movies-errors').append('

There was something wrong with your request!

') + } + }); + } + }, + matchingMovies(event) { + event.preventDefault(); + this.$('#matching-movies').empty(); + + const movieTitle = this.getFormData(); + this.clearFormData(); + + // Set URL + this.model.url += movieTitle.title; + + const results = this.model.fetch({ + success: (model, response) => { + response.forEach((movieData) => { + let newMovie = new ReturnedMovie(movieData); + + if (newMovie.isValid()) { + let returnedMovieView = new ReturnedMovieView({ + tagName: 'tr', + template: this.template, + model: newMovie, + bus: this.bus, + }); + this.$('#matching-movies').append(returnedMovieView.render().$el); + } + }); + }, + error: (model, response) => { + this.$('#returned-movies-errors').append('

There was something wrong with your request!

') + } + }); + // Reset URL + this.model.url = 'http://localhost:3000/movies/?query='; + }, + + getFormData() { + const data = {}; + data['title'] = this.$('form input[name=title]').val(); + return data; + }, + + clearFormData() { + this.$('form input[name=title]').val(''); + }, +}); // ReturnedMovieListView + +export default ReturnedMovieListView; diff --git a/src/views/returned_movie_view.js b/src/views/returned_movie_view.js new file mode 100644 index 000000000..a93a75261 --- /dev/null +++ b/src/views/returned_movie_view.js @@ -0,0 +1,26 @@ +import Backbone from 'backbone'; +import ReturnedMovie from '../models/returned_movie'; +import $ from 'jquery'; + +const ReturnedMovieView = Backbone.View.extend({ + model: ReturnedMovie, + + initialize(params) { + this.template = params.template; + this.bus = params.bus; + }, + events: { + 'click .btn-add': 'sendMovieData' + }, + render() { + const compiledTemplate = this.template(this.model.toJSON()); + this.$el.html(compiledTemplate); + return this; + }, + // Triggers the function in Movie List View + sendMovieData: function(){ + this.bus.trigger('addMovie', this.model.attributes); + }, +}); + +export default ReturnedMovieView;