diff --git a/dist/index.html b/dist/index.html index b873b1e..e54259e 100644 --- a/dist/index.html +++ b/dist/index.html @@ -7,14 +7,118 @@
+

backTrek

+

~going the distance~

+
+ + +
+ +
+ +
+
+ + + +
+

Add a Trip

+
+ + + + + + + + + + + + + + + + + + + +
+
+ +
+ + + + + + + + + + +
namecontinentcategoryweekscost
+
+
+ + + + + + + + + diff --git a/src/app.js b/src/app.js index e7af594..5b0cfb3 100644 --- a/src/app.js +++ b/src/app.js @@ -6,8 +6,165 @@ import _ from 'underscore'; import './css/foundation.css'; import './css/style.css'; -console.log('it loaded!'); +import TripList from 'app/collections/trip_list'; +import Trip from 'app/models/trip'; -$(document).ready( () => { - $('main').html('

Hello World!

'); -}); + +const TRIP_FIELDS = ['name', 'continent', 'category', 'weeks', 'cost']; + +// Clear status messages +const clearStatus = function clearStatus() { + $('#status-messages ul').html(''); + $('#status-messages').hide(); +}; + +// Add a new status message +const reportStatus = function reportStatus(status, message) { + console.log(`Reporting ${ status } status: ${ message }`); + + // Should probably use an Underscore template here. + const statusHTML = `
  • ${ message }
  • `; + + // note the symetry with clearStatus() + $('#status-messages ul').append(statusHTML); + $('#status-messages').show(); +}; + +const tripList = new TripList(); +// initalize templates +let listTemplate; +let tripTemplate; +let reserveTemplate; + +const renderTrips = function renderTrips(list) { + const tripTableElement = $('#trip-list'); + tripTableElement.html(''); + console.log(`tripList :${list}`); + list.forEach((trip) => { + const generatedHTML = $(listTemplate(trip.attributes)); + // adding event handler here gives context as to which trip needs to be clicked + generatedHTML.on('click', (event) => { + const tripDetails = new Trip({id: trip.get('id')}) + // grab backbone object and pass into function + + tripDetails.fetch( + { + success: function(trip) { + console.log('worked'); + renderTrip(trip); + }, + error: function(message, b) { + console.log(message, b); + } + }); // end of fetch + }) + tripTableElement.append(generatedHTML); + }); + }; + + // BUG: renderReserve not firing on submit, console.logs not logging + const renderReserve = function renderReserve() { + const reserveForm = $('#reserve-form'); + reserveForm.html(''); + const generatedHTML = reserveTemplate(); + console.log(generatedHTML); + reserveForm.append(generatedHTML); + } + + const renderTrip = function renderTrip(trip) { + const tripElement = $('#trip'); + // clears html in tripElement + tripElement.html(''); + const generatedHTML = tripTemplate(trip.attributes); + tripElement.append(generatedHTML); + + $('#reserve').on('click', (event) => { + renderReserve(); + }); + } + + $('th.sort').removeClass('current-sort-field'); + $(`th.sort.${tripList.comparator}`).addClass('current-sort-field'); + + const readFormData = function readFormData() { + const tripData = {}; + + TRIP_FIELDS.forEach((field) => { + + const inputElement = $(`#add-trip-form input[name="${field}"]`); + const value = inputElement.val(); + + if (value != '') { + tripData[field] = value; + } + inputElement.val(''); + }); + console.log("reading trip data"); + console.log(tripData); + return tripData; + }; + + const outputValidationFailures = function outputValidationFailures(errors) { + for (let field in errors) { + for (let problem of errors[field]) { + reportStatus('error', `${field}: ${problem}`); + } + } + }; + const addTripHandler = function(event) { + event.preventDefault(); + + const trip = new Trip(readFormData()); + + if (!trip.isValid()) { + outputValidationFailures(trip.validationError); + return; + } + tripList.add(trip); + + trip.save({}, { + success: (model, response) => { + console.log('successfully saved trip'); + reportStatus('success', 'Successfully saved trip!'); + }, + error: (model, response) => { + console.log('failed to save trip'); + console.log(response); + // removes from list if fails validations + tripList.remove(model); + outputValidationFailures(response.responseJSON["errors"]); + }, + }); + }; + + $(document).ready( () => { + // compile underscore templates + listTemplate = _.template($('#list-template').html()); + tripTemplate = _.template($('#trip-template').html()); + reserveTemplate = _.template($('#reserve-template').html()); + + tripList.on('sort', renderTrips); + tripList.fetch(); + + $('#add-trip-form').on('submit', addTripHandler); + + $('#trips').on('click', () => { + $('#trip-list').toggle(); + renderTrips(tripList); + }); + + $('#add-trip').on('click', () => { + console.log('clicked add trip'); + $('.add-trip-form').toggle(); + }); + + TRIP_FIELDS.forEach((field) => { + const headerElement = $(`th.sort.${field}`); + headerElement.on('click', (event) => { + console.log(`sorting table by ${field}`); + tripList.comparator = field; + tripList.sort(); + }); + }); + $('#status-messages').on('click', clearStatus); + }); // end doc.ready diff --git a/src/app/collections/trip_list.js b/src/app/collections/trip_list.js new file mode 100644 index 0000000..ee7e18a --- /dev/null +++ b/src/app/collections/trip_list.js @@ -0,0 +1,14 @@ +import Backbone from 'backbone' +import Trip from '../models/trip'; + +const TripList = Backbone.Collection.extend({ + model: Trip, + url: 'https://ada-backtrek-api.herokuapp.com/trips', + parse: function(response) { + return response; + }, + comparator: 'name', +}); + + +export default TripList; diff --git a/src/app/models/reservation.js b/src/app/models/reservation.js new file mode 100644 index 0000000..b6e8fa4 --- /dev/null +++ b/src/app/models/reservation.js @@ -0,0 +1,9 @@ +import Backbone from 'backbone'; + +const Reservation = Backbone.Model.extend({ + url: function () { + return `https://ada-backtrek-api.herokuapp.com/trips/${this.get('id')}/reservations`; + } +}); + +export default Reservation; diff --git a/src/app/models/trip.js b/src/app/models/trip.js new file mode 100644 index 0000000..e1a6e27 --- /dev/null +++ b/src/app/models/trip.js @@ -0,0 +1,51 @@ +import Backbone from 'backbone' + +const Trip = Backbone.Model.extend({ + urlRoot: 'https://ada-backtrek-api.herokuapp.com/trips', + // urlRoot() { + // return `https://ada-backtrek-api.herokuapp.com/trips/${ this.get('id') }`; + // }, + toString() { + return ``; + }, + validate(attributes) { + // Note the argument. We will read attribute values from + // here instead of calling this.get() + + // Format of errors: same as Rails! + // { + // title: ['cannot be blank', 'already taken'], + // author: ['cannot be blank'] + // } + + const errors = {}; + if (!attributes.name) { + errors.name = ['cannot be blank']; + } + + if (!attributes.continent) { + errors.continent = ['cannot be blank']; + } + + if (!attributes.category) { + errors.category = ['cannot be blank']; + } + + if (!attributes.weeks) { + errors.weeks = ['cannot be blank']; + } + + if (!attributes.cost) { + errors.cost = ['cannot be blank']; + } + + + if (Object.keys(errors).length < 1) { + return false; + } + return errors; + }, + +}); + +export default Trip; diff --git a/src/css/style.css b/src/css/style.css index b7b2d44..81e6a0f 100644 --- a/src/css/style.css +++ b/src/css/style.css @@ -1,2 +1,13 @@ /* Your styles go here! */ + +body { + /*background-image: url(https://images.unsplash.com/photo-1470071459604-3b5ec3a7fe05?auto=format&fit=crop&w=1440&q=80&ixid=dW5zcGxhc2guY29tOzs7Ozs%3D);*/ + +} +header { + color: white; + background-color: #ca412b; + height: 200px; + text-align: center; +}