diff --git a/dist/index.html b/dist/index.html index 559b18ecd..48a2b50da 100644 --- a/dist/index.html +++ b/dist/index.html @@ -3,11 +3,53 @@ Backbone Baseline +
- + + + +
+
+ + +
+ +
+ +
+

Search Results

+ +
+ +
+

Store Library

+ + +
+
+ + + diff --git a/package-lock.json b/package-lock.json index 2d3371d51..a9569be1a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -5036,6 +5036,21 @@ "esprima": "2.7.3" } }, + "jscroll": { + "version": "2.3.9", + "resolved": "https://registry.npmjs.org/jscroll/-/jscroll-2.3.9.tgz", + "integrity": "sha512-eb9rc/QNMmd+AP6aeoNXKe1Lv0ISV6F9edt2Onku15UpEb2PJqxkwudAe3J/IN7R0bFLvWNpw+wDssXEgrie9w==", + "requires": { + "jquery": "1.12.4" + }, + "dependencies": { + "jquery": { + "version": "1.12.4", + "resolved": "https://registry.npmjs.org/jquery/-/jquery-1.12.4.tgz", + "integrity": "sha1-AeHfuikP5z3rp3zurLD5ui/sngw=" + } + } + }, "jsesc": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-1.3.0.tgz", @@ -6828,6 +6843,12 @@ "unpipe": "1.0.0" } }, + "raw-loader": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/raw-loader/-/raw-loader-0.5.1.tgz", + "integrity": "sha1-DD0L6u2KAclm2Xh793goElKpeao=", + "dev": true + }, "read-pkg": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-2.0.0.tgz", @@ -7211,6 +7232,15 @@ "ajv": "5.5.1" } }, + "script-loader": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/script-loader/-/script-loader-0.7.2.tgz", + "integrity": "sha512-UMNLEvgOAQuzK8ji8qIscM3GIrRCWN6MmMXGD4SD5l6cSycgGsCo0tX5xRnfQcoghqct0tjHjcykgI1PyBE2aA==", + "dev": true, + "requires": { + "raw-loader": "0.5.1" + } + }, "select-hose": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/select-hose/-/select-hose-2.0.0.tgz", diff --git a/package.json b/package.json index 97144b128..7ae695e6b 100644 --- a/package.json +++ b/package.json @@ -51,6 +51,7 @@ "jasmine-core": "^2.8.0", "jasmine-expect": "^3.8.1", "module-resolver": "^1.0.0", + "script-loader": "^0.7.2", "style-loader": "^0.19.0", "webpack": "^3.8.1", "webpack-dev-server": "^2.9.4" @@ -59,6 +60,7 @@ "backbone": "^1.3.3", "foundation-sites": "^6.4.4-rc1", "jquery": "^3.2.1", - "underscore": "^1.8.3" + "underscore": "^1.8.3", + "jscroll": "*" } } diff --git a/src/app.js b/src/app.js index 30c00d594..9d5b1bc8d 100644 --- a/src/app.js +++ b/src/app.js @@ -3,12 +3,56 @@ import 'foundation-sites/dist/css/foundation.css'; import './css/styles.css'; // Import jQuery & Underscore -import $ from 'jquery'; import _ from 'underscore'; +import $ from 'jquery'; + +import StoreLibrary from 'collections/store_library'; +import StoreMovie from 'models/store_movie'; +import APIMovies from 'collections/api_movies'; +import APIMovie from 'models/api_movie'; + +import StoreLibraryView from 'views/store_library_view'; +import StoreMovieView from 'views/store_movie_view'; +import APIMoviesView from 'views/api_movies_view'; +import APIMovieView from 'views/api_movie_view'; -// ready to go $(document).ready(function() { - $('#main-content').append('

Hello World!

'); + $('.api-movies-container').hide(); + + const storeLibrary = new StoreLibrary(); + storeLibrary.fetch(); + + const storeLibraryView = new StoreLibraryView({ + model: storeLibrary, + template: _.template($('#store-movie-template').html()), + el: 'main' + }); + storeLibraryView.render(); + + const apiMovies = new APIMovies(); + + const apiMoviesView = new APIMoviesView({ + model: apiMovies, + template: _.template($('#api-movie-template').html()), + el: 'main' + }); + apiMoviesView.storeLibrary = storeLibrary; + $('.submit-btn').on('click', function(e) { + e.preventDefault(); + $('.errors').empty(); + const query = $('input[name=query]').val(); + apiMovies.fetch({data: {query:`${query}`}}).then(function(){ + if (apiMoviesView.model.length === 0) { + $('.errors').append('
  • There are no movies with that keyword search.
  • '); + $('.api-movies-container').hide(); + } else { + $('.api-movies-container').show(); + apiMoviesView.render(); + console.log(apiMovies.length); + } + }); + }); + $('.store-library-container').jscroll(); }); diff --git a/src/collections/api_movies.js b/src/collections/api_movies.js new file mode 100644 index 000000000..d0282bee2 --- /dev/null +++ b/src/collections/api_movies.js @@ -0,0 +1,19 @@ +import Backbone from 'backbone'; +import APIMovie from 'models/api_movie'; +import StoreMovie from 'models/store_movie'; +import StoreLibrary from 'collections/store_library'; + +const APIMovies = Backbone.Collection.extend({ + model: APIMovie, + sync: function(method, model, options) { + switch(method) { + case 'read': + options.url = 'http://localhost:3000/movies'; + + // options.url = 'http://localhost:3000/movies/' + 'search'; + return Backbone.sync(method, model, options); + } + } +}); + +export default APIMovies; diff --git a/src/collections/store_library.js b/src/collections/store_library.js new file mode 100644 index 000000000..5fb1b3913 --- /dev/null +++ b/src/collections/store_library.js @@ -0,0 +1,15 @@ +import Backbone from 'backbone'; +import StoreMovie from 'models/store_movie'; + +const StoreLibrary = Backbone.Collection.extend({ + model: StoreMovie, + sync: function(method, model, options) { + switch(method) { + case 'read': + options.url = 'http://localhost:3000/movies'; + return Backbone.sync(method, model, options); + } + } +}); + +export default StoreLibrary; diff --git a/src/css/styles.css b/src/css/styles.css index 68a79a569..b885a4b70 100644 --- a/src/css/styles.css +++ b/src/css/styles.css @@ -1,44 +1,90 @@ @include foundation-everything; main { - background: lightblue; + background-image: url('https://i.pinimg.com/originals/2c/6b/a3/2c6ba31d76936549fb3376552cb4070c.jpg'); + background-size: 1000px 1000px; + font-family: 'Londrina Solid', cursive; } -header { - background-color: lightgreen; - padding: 0.5rem; +button:hover { + background-color: #e22ef7; } -#completed-checkbox { +div { display: inline; } -label { - display: inline; +#search-form, .errors { + margin-right: 20%; + margin-left: 20%; + width: 60%; + text-align: center; } -button.success { - margin-right: 1.2rem; - display: inline; +.logo { + font-size: 5rem; + text-align: center; + /*text-shadow: 10px 10px #f7add3;*/ } -aside.create-tasklist { - background-color: navy; - color: #FFFFFF; +#store-library, #api-movies { + margin-right: 10%; + margin-left: 10%; + display: inline-flex; + flex-flow: row; + flex-wrap: wrap; + justify-content: center; } -aside label { - color: #FFFFFF; + +.logo, .api-movies-container h2, .store-library-container h2 { + color: #f4c842; + text-shadow: 6px 4px 0 #000; + background: url('https://i.pinimg.com/736x/41/79/3a/41793a2a1b0406da3f4e0d29b2e3de3a--pattern-print-print-patterns.jpg'); + -webkit-background-clip: text; + -webkit-text-fill-color: transparent; + font-family: 'Londrina Solid', cursive; } -.completed { - text-decoration: line-through; +.api-movies-container h2, .store-library-container h2 { + text-align: center; + text-transform: uppercase; + font-size: 4rem; + text-shadow: 6px 4px 0 #e22ef7; } -div { - display: inline; +.errors { + color: white; + font-size: 2rem; + list-style: none; +} +.title { + display: block; +} + +.single-api-movie, .single-store-movie { + margin: 1em; + box-shadow: 13px 13px #08c4d0; + position: relative; } -/* -* { + +.single-api-movie img, .single-store-movie img{ border-style: solid; + border-width: medium; + border-color: #2eebf7; + width: 185px; +} + +#submit-button:hover, #add-movie-btn:hover { + background-color: #2eebf7; +} + +.add-movie-btn { + position: absolute; + background-color: #e22ef7; + bottom: 10px; + right: 10px; +} + +.submit-btn { + background-color: #e22ef7; } -*/ diff --git a/src/css/stylesheet.css b/src/css/stylesheet.css new file mode 100644 index 000000000..f854a404c --- /dev/null +++ b/src/css/stylesheet.css @@ -0,0 +1,48 @@ +/*! Generated by Font Squirrel (https://www.fontsquirrel.com) on December 20, 2017 */ + + + +@font-face { + font-family: 'kirvyregular'; + src: url('kirvy-regular-webfont.woff2') format('woff2'), + url('kirvy-regular-webfont.woff') format('woff'); + font-weight: normal; + font-style: normal; + +} + + + + +@font-face { + font-family: 'gravitylight'; + src: url('gravity-light-webfont.woff2') format('woff2'), + url('gravity-light-webfont.woff') format('woff'); + font-weight: normal; + font-style: normal; + +} + + + + +@font-face { + font-family: 'gravityregular'; + src: url('gravity-regular-webfont.woff2') format('woff2'), + url('gravity-regular-webfont.woff') format('woff'); + font-weight: normal; + font-style: normal; + +} + + + + +@font-face { + font-family: 'geosanslightregular'; + src: url('geosanslight-webfont.woff2') format('woff2'), + url('geosanslight-webfont.woff') format('woff'); + font-weight: normal; + font-style: normal; + +} diff --git a/src/models/api_movie.js b/src/models/api_movie.js new file mode 100644 index 000000000..1b1f98d9e --- /dev/null +++ b/src/models/api_movie.js @@ -0,0 +1,20 @@ +import Backbone from 'backbone'; + +import APIMovies from 'collections/api_movies'; + +const APIMovie = Backbone.Model.extend({ + + sync: function(method, model, options) { + switch(method) { + case 'read': + options.url = 'http://localhost:3000/movies'; + return Backbone.sync(method, model, options); + } + } + // idAttribute: 'title', + // urlRoot: 'http://localhost:3000/movies', + + +}); + +export default APIMovie; diff --git a/src/models/store_movie.js b/src/models/store_movie.js new file mode 100644 index 000000000..371609688 --- /dev/null +++ b/src/models/store_movie.js @@ -0,0 +1,27 @@ +import Backbone from 'backbone'; + +const StoreMovie = Backbone.Model.extend({ + validate(attributes) { + const errors = {}; + if (!attributes.title) { + errors['title'] = ['You need a movie with a title.']; + } + if ( Object.keys(errors).length > 0 ) { + return errors; + } else { + return false; + } + }, + sync: function(method, model, options) { + switch(method) { + case 'read': + options.url = 'http://localhost:3000/movies' + model.get('title'); + return Backbone.sync(method, model, options); + case 'create': + options.url = 'http://localhost:3000/movies'; + return Backbone.sync(method, model, options); + } + } +}); + +export default StoreMovie; diff --git a/src/views/api_movie_view.js b/src/views/api_movie_view.js new file mode 100644 index 000000000..265fabaa9 --- /dev/null +++ b/src/views/api_movie_view.js @@ -0,0 +1,74 @@ +import Backbone from 'backbone'; +import $ from 'jquery'; +import _ from 'underscore'; + +// import StoreMovie from 'models/store_movie'; +// import StoreMoviesView from 'models/store_movies'; +import APIMovie from 'models/api_movie' +import StoreMovie from 'models/store_movie' + +import APIMoviesView from '../views/api_movies_view' +import StoreMovieView from '../views/store_movie_view'; + +import APIMovies from 'collections/api_movies' +import StoreLibrary from '/collections/store_library'; + +const APIMovieView = Backbone.View.extend({ + initialize(params) { + this.template = params.template; + this.listenTo(this.model, "change", this.render); + }, + render() { + const compiledTemplate = this.template(this.model.toJSON()); + this.$el.html(compiledTemplate); + return this; + }, + events: { + 'click .add-movie-btn': 'addMovieToLibrary', + }, + + addMovieToLibrary: function() { + + const newStoreMovie = new StoreMovie({ + title: this.model.attributes.title, + release_date: this.model.attributes.release_date, + overview: this.model.attributes.overview, + image_url: this.model.attributes.image_url, + }); + + let duplicate = false; + this.storeLibrary.forEach(function(storeMovie) { + if (storeMovie.attributes.title === newStoreMovie.attributes.title) { + duplicate = true; + } + }); + if (duplicate === true) { + this.failureMessages({duplicate: ['This film is already in your library.']}); + } else { + if (newStoreMovie.isValid()) { + newStoreMovie.save(); + this.storeLibrary.add(newStoreMovie); + this.$el.empty(); + this.successMessages(`${newStoreMovie.attributes.title} has been successfully added to your rental library.`); + } else + this.failureMessages(newStoreMovie.validationError); + } + }, + + failureMessages: function(messageHash) { + const statusMessagesEl = $('.errors'); + statusMessagesEl.empty(); + _.each(messageHash, (messageType) => { + messageType.forEach((message) => { + statusMessagesEl.append(`
  • ${message}
  • `); + }) + }); + }, + successMessages: function(message) { + const statusMessagesEl = $('.errors'); + statusMessagesEl.empty(); + statusMessagesEl.append(`
  • ${message}
  • `); + }, +}); + +export default APIMovieView; diff --git a/src/views/api_movies_view.js b/src/views/api_movies_view.js new file mode 100644 index 000000000..bba501442 --- /dev/null +++ b/src/views/api_movies_view.js @@ -0,0 +1,35 @@ +import Backbone from 'backbone'; + +import _ from 'underscore'; +import $ from 'jquery'; + +// import StoreMovieView from '../views/store_movie_view'; +// import StoreMovie from 'models/store_movie'; +// import StoreMovies from '/collections/store_movies'; +// import StoreMoviesView from 'models/store_movies'; +import APIMovieView from '../views/api_movie_view'; +import APIMovie from 'models/api_movie'; +import APIMovies from 'collections/api_movies'; + + +const APIMoviesView = Backbone.View.extend({ + initialize(params) { + this.template = params.template; + this.listenTo(this.model, 'update', this.render); + }, + render() { + this.$('#api-movies').empty(); + this.model.each((apiMovie) => { + const apiMovieView = new APIMovieView({ + model: apiMovie, + template: this.template, + className: 'apiMovie', + }); + apiMovieView.storeLibrary = this.storeLibrary; + this.$('#api-movies').append(apiMovieView.render().$el); + }); + return this; + }, +}); + +export default APIMoviesView; diff --git a/src/views/store_library_view.js b/src/views/store_library_view.js new file mode 100644 index 000000000..782a36138 --- /dev/null +++ b/src/views/store_library_view.js @@ -0,0 +1,26 @@ +import Backbone from 'backbone'; +import StoreMovieView from '../views/store_movie_view'; +import StoreMovie from 'models/store_movie'; + + +const StoreLibraryView = Backbone.View.extend({ + initialize(params) { + this.template = params.template; + this.listenTo(this.model, 'update', this.render); + this.listenTo(this.model, 'change', this.render); + }, + render() { + this.model.each((storeMovie) => { + const storeMovieView = new StoreMovieView({ + model: storeMovie, + template: this.template, + className: 'storeMovie', + }); + this.$('#store-library').append(storeMovieView.render().$el); + }); + return this; + }, + +}); + +export default StoreLibraryView; diff --git a/src/views/store_movie_view.js b/src/views/store_movie_view.js new file mode 100644 index 000000000..c739979d2 --- /dev/null +++ b/src/views/store_movie_view.js @@ -0,0 +1,17 @@ +import Backbone from 'backbone'; +import $ from 'jquery'; +import _ from 'underscore'; + +const StoreMovieView = Backbone.View.extend({ + initialize(params) { + this.template = params.template; + this.listenTo(this.model, "change", this.render); + }, + render() { + const compiledTemplate = this.template(this.model.toJSON()); + this.$el.html(compiledTemplate); + return this; + }, +}); + +export default StoreMovieView;