diff --git a/dist/index.html b/dist/index.html index b873b1e..52eda26 100644 --- a/dist/index.html +++ b/dist/index.html @@ -2,19 +2,108 @@ - My JavaScript App + Aventura + -
- +
+

Camino de Aventura

+
-
+
+
+
+
+
+
+
+ +
+ +
+
+ + + + + + + + + + +
NameCategoryContinentWeeksCost
+
+
+
+
+
+
+
+

Add a New Trip

+ + + + + + + + + + + + + +
+
+
+
+ + + + + + + +
+
-
+ + diff --git a/package-lock.json b/package-lock.json index f830985..74b17bc 100644 --- a/package-lock.json +++ b/package-lock.json @@ -5017,6 +5017,11 @@ "resolved": "https://registry.npmjs.org/jquery/-/jquery-3.2.1.tgz", "integrity": "sha1-XE2d5lKvbNCncBVKYxu6ErAVx4c=" }, + "jquery-modal": { + "version": "0.9.1", + "resolved": "https://registry.npmjs.org/jquery-modal/-/jquery-modal-0.9.1.tgz", + "integrity": "sha512-04C0nhhFBDkFt3VUQaZDOXWPifpJHm8JjhgydePV1drI9ZvOTkIRLVOzCy2mo5/bcWqPU8UhLq4L6Pdex//lvw==" + }, "js-base64": { "version": "2.3.2", "resolved": "https://registry.npmjs.org/js-base64/-/js-base64-2.3.2.tgz", diff --git a/package.json b/package.json index 5860ddc..9035c63 100644 --- a/package.json +++ b/package.json @@ -58,6 +58,7 @@ "dependencies": { "backbone": "^1.3.3", "jquery": "^3.2.1", + "jquery-modal": "^0.9.1", "underscore": "^1.8.3" } } diff --git a/src/app.js b/src/app.js index e7af594..89b5edd 100644 --- a/src/app.js +++ b/src/app.js @@ -1,13 +1,218 @@ // Vendor Modules import $ from 'jquery'; import _ from 'underscore'; +import 'jquery-modal'; // CSS import './css/foundation.css'; import './css/style.css'; -console.log('it loaded!'); +//MODELS AND COLLECTIONS +import Trip from './app/models/trip'; +import TripList from './app/collections/trip_list'; +import Reservation from './app/models/reservation'; + +const $tripsList = $('#trips-list') +const $tripDescription = $('#trip-description') +const $newTripBtn = $('#newTripBtn'); +const $addTripForm = $('#add-trip-form'); +const $resForm = $('#reservation-form'); +const $queryValue = $('#query-value'); +const $statusMessages = $('#status-messages') +const $sort = $('.sort'); +const $resModal = $('#res-modal'); + +//templates +let tripTemplate; +let tripDetailsTemplate; + +// create tripList collection of trips +let tripList = new TripList(); + +// fields that exist for a trip in the Trek API +const fields = ['name', 'category', 'continent', 'weeks', 'cost', 'about', 'tripID']; + +// render trips table +const render = function render(tripList) { + tripDetailsTemplate = _.template($('#trip-details-template').html()); + + $tripsList.empty(); + tripList.forEach((trip) => { + $tripsList.append(tripTemplate(trip.attributes)); + }); +} + +const renderErrors = (errors) => { + $statusMessages.empty(); + Object.keys(errors).forEach((error) => { + $statusMessages.append(`

${errors[error]}

`); + }) + $statusMessages.css('display', 'block'); +} + +const events = { + showNewTripForm() { + $('#new-trip-modal').css('display', 'block'); + }, + addTrip(event){ + event.preventDefault(); + const tripData = {}; + + fields.forEach( (field) => { + const val = $(`input[name=${field}]`).val(); + if (val !== '' ) { + tripData[field] = val; + } + }); + + const trip = new Trip(tripData); + + if (trip.isValid()) { + trip.save({}, { + success: events.successfullSave, + error: events.failedSave, + }); + } else { + renderErrors(trip.validationError); + } + }, + successfullSave(trip) { + $statusMessages.empty(); + $statusMessages.append(`

${trip.get('name')} added!

`); + $statusMessages.css('display', 'block'); + }, + failedSave(trip, response) { + renderErrors(response.responseJSON.errors); + trip.destroy(); + }, + successReservation(reservation) { + $statusMessages.empty(); + $statusMessages.append(`

${reservation.get('name')} added!

`); + $statusMessages.css('display', 'block'); + }, + failedReservation(reservation, response) { + renderErrors(response.responseJSON.errors) + reservation.destroy(); + }, + sortTrips() { + $('.current-sort-field').removeClass('current-sort-field'); + + // get the class list of the selected element + const classes = $(this).attr('class').split(/\s+/); + + classes.forEach((className) => { + if (fields.includes(className)) { + if (className === tripList.comparator) { + tripList.models.reverse(); + tripList.trigger('sort', tripList); + } else { + tripList.comparator = className; + tripList.sort(); + } + } + }); + + $('.current-sort-field').removeClass('current-sort-field'); + $(this).addClass('current-sort-field'); + }, + filterTrips(event) { + event.preventDefault(); + const $tripQuery = $('#trip-query option:selected'); + const query = $tripQuery.val(); + + const $queryValue = $('#query-value'); + const queryValue = $queryValue.val(); + let filteredTrips = tripList; + + if (query == 'weeks' || query == 'cost') { + filteredTrips = tripList.filter(trip => trip.get(query) <= queryValue); + } else { + filteredTrips = tripList.filter(function(trip) { + const attr_value = trip.attributes[query].toLowerCase(); + const search_term = queryValue.toLowerCase(); + + if (attr_value.includes(search_term)) { + return true; + } + }); + } + render(filteredTrips); + }, + clearModals() { + const modal = document.getElementById('res-modal'); + const modal2 = document.getElementById('new-trip-modal'); + const modal3 = document.getElementById('status-messages-modal'); + + if (event.target == modal || event.target == modal2 || event.target == modal3) { + (event.target).style.display = 'none'; + modal3.style.display = 'none'; + } + }, + getTrip() { + const trip = new Trip({ id: $(this).attr('data-id') }) + $tripDescription.empty(); + trip.fetch().done(() => { + $tripDescription.append(tripDetailsTemplate(trip.attributes)); + }); + }, + showResForm() { + const tripID = $(this).attr('data-id'); + $resForm.append(``); + $resModal.css('display', 'block'); + }, + addReservation() { + event.preventDefault(); + const resData = {}; + const formfields = ['name', 'age', 'email', 'tripID'] + + formfields.forEach( (field) => { + const val = $(`form input[name=res-${field}]`).val(); + if (val !== '') { + resData[field] = val; + } + }); + + const reservation = new Reservation(resData) + + if (reservation.isValid()) { + reservation.save({}, { + success: events.successReservation, + error: events.failedReservation, + }); + } else { + renderErrors(reservation.validationError); + } + } +} $(document).ready( () => { - $('main').html('

Hello World!

'); + tripTemplate = _.template($('#trip-template').html()); + + // Clicking Events + $sort.click(events.sortTrips); + $(document).on('click', events.clearModals); + $tripsList.on('click', 'tr', events.getTrip); + $tripDescription.on('click', 'button', events.showResForm); + $newTripBtn.on('click', events.showNewTripForm) + + // Form Submitting Events + $addTripForm.submit(events.addTrip); + $resForm.submit(events.addReservation); + + // keyed events + $queryValue.on('keyup', events.filterTrips); + + // Backbone Events + tripList.on('update', render, tripList); + tripList.on('sort', render, tripList); + + // Implement when page loads + tripList.fetch() + + const trip = new Trip({ id: 3 }); + trip.fetch().done(() => { + $tripDescription.append('

FEATURED TRIP

'); + $tripDescription.append(tripDetailsTemplate(trip.attributes)); + }); + }); diff --git a/src/app/collections/trip_list.js b/src/app/collections/trip_list.js new file mode 100644 index 0000000..52336ac --- /dev/null +++ b/src/app/collections/trip_list.js @@ -0,0 +1,12 @@ +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(response) { + return response + }, +}); + +export default TripList; diff --git a/src/app/models/reservation.js b/src/app/models/reservation.js new file mode 100644 index 0000000..1003b08 --- /dev/null +++ b/src/app/models/reservation.js @@ -0,0 +1,30 @@ +import Backbone from 'backbone'; + +const Reservation = Backbone.Model.extend({ + url: function() { + return `https://ada-backtrek-api.herokuapp.com/trips/${this.attributes.tripID}/reservations` + }, + + validate: function(attributes) { + const errors = {}; + + if (!attributes.name) { + errors['name'] = 'Name cannot be blank'; + } + if (!attributes.age) { + errors['age'] = 'Age cannot be blank'; + } else if (isNaN(attributes.age)) { + errors['age'] = 'Age must be a number'; + } + if (!attributes.email) { + errors['email'] = 'Email cannot be blank'; + } + if (Object.keys(errors).length > 0) { + return errors; + } else { + return false; + } + } +}); + +export default Reservation diff --git a/src/app/models/trip.js b/src/app/models/trip.js new file mode 100644 index 0000000..5d6515c --- /dev/null +++ b/src/app/models/trip.js @@ -0,0 +1,46 @@ +import Backbone from 'backbone'; + +const Trip = Backbone.Model.extend({ + urlRoot: 'https://ada-backtrek-api.herokuapp.com/trips', + parse(response) { + return response + }, + validate: function(attributes) { + const errors = {}; + const continents = ['Africa', 'Antartica', 'Asia', 'Australasia', 'Europe', 'North America', 'South America'] + + if (!attributes.name) { + errors['name'] = 'Name cannot be blank'; + } + + if (!attributes.continent) { + errors['continent'] = 'continent cannot be blank'; + } else if (!continents.includes(attributes.continent)) { + errors['continent'] = 'continent must be from the dropdown list'; + } + + if (!attributes.category) { + errors['category'] = 'category cannot be blank'; + } + + if (!attributes.weeks) { + errors['weeks'] = 'weeks cannot be blank'; + } else if (isNaN(attributes.weeks)){ + errors['weeks'] = 'weeks must be number'; + } + + if (!attributes.cost) { + errors['cost'] = 'cost cannot be blank'; + } else if (isNaN(attributes.cost)) { + errors['cost'] = 'cost must be a number'; + } + + if (Object.keys(errors).length > 0) { + return errors; + } else { + return false; + } + }, +}); + +export default Trip diff --git a/src/css/style.css b/src/css/style.css index b7b2d44..3871dd3 100644 --- a/src/css/style.css +++ b/src/css/style.css @@ -1,2 +1,58 @@ /* Your styles go here! */ +header { + width: 95%; + margin-top: 50px; + margin-bottom: 75px; +} + +.row { + max-width: 95%; +} + +.newTripBtn { + height: 100%; +} + +#trip-description { + padding-right: 40px; +} + +.current-sort-field { + background-color: blue; + color: white; +} +/*#status-messages { + background-color: #dfdfdf; + display: none; +}*/ + +#res-modal, #new-trip-modal, #status-messages { + display: none; + position: fixed; + z-index: 990; + top: 0; + left: 0; + width: 100%; + height: 100%; + background: black; + background: rgba(0,0,0,0.5); +} + +#reservation-form, #add-trip-form, #status-messages { + background: white; + position: fixed; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + padding: 30px; + width: 400px; + border-radius: 20px; +} + +#status-messages { + z-index: 999; + height: auto; + border: 1px solid black; + background-color: yellow; +} diff --git a/src/lake.JPG b/src/lake.JPG new file mode 100644 index 0000000..e019073 Binary files /dev/null and b/src/lake.JPG differ