Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
45 commits
Select commit Hold shift + click to select a range
7b7d9b7
got trip list from api
skwiens Nov 28, 2017
3204ab5
trips showing in table
skwiens Nov 28, 2017
aa3cb63
html changes
skwiens Nov 29, 2017
cd1f76e
get trip from api and trip details template
skwiens Nov 29, 2017
474dada
trip details showing
skwiens Nov 29, 2017
bba01e7
js clean-up
skwiens Nov 29, 2017
bc8dd5a
button added to html
skwiens Nov 29, 2017
1447cb8
click event created
skwiens Nov 29, 2017
a4f4155
posts trip
skwiens Nov 30, 2017
5f75e27
api validation error messages showing
skwiens Nov 30, 2017
cc1ca4f
valid and invalid trip client side
skwiens Nov 30, 2017
41ad2b1
errors from invalid trip on client side before api request, still pos…
skwiens Nov 30, 2017
e6e2929
html form requirements added
skwiens Nov 30, 2017
ce16aef
reservation model added
skwiens Nov 30, 2017
090fe55
reservation form in html
skwiens Nov 30, 2017
3563711
show reservation form with hidden field trip id
skwiens Nov 30, 2017
af10d74
form data taken in, reservation created
skwiens Dec 1, 2017
25030da
validations added to reservation
skwiens Dec 1, 2017
cf59a32
reservation can be made to api
skwiens Dec 1, 2017
93a5c6a
uncommented needed code
skwiens Dec 1, 2017
657ea12
sort and reverse sort
skwiens Dec 1, 2017
cb57884
sorting
skwiens Dec 1, 2017
6540b11
form with selector to select category
skwiens Dec 1, 2017
98d5f73
filtering some stuff
skwiens Dec 1, 2017
63b108e
new trip modal added
skwiens Dec 2, 2017
39dd166
catch filter up to other changes made in other branches
skwiens Dec 2, 2017
620d0e8
now searches by includes instead of exact matches
skwiens Dec 2, 2017
2ad9f93
filters regardless of case
skwiens Dec 2, 2017
0d72c36
search by less than or equal to for cost and weeks
skwiens Dec 2, 2017
8cd245a
cleanup
skwiens Dec 2, 2017
9eeaff2
columns and filter fields on the same line
skwiens Dec 3, 2017
09ed5a1
clean up some console.logs and make some global jQuery variables
skwiens Dec 3, 2017
09e0e73
error messages are paragraphs instead of lists
skwiens Dec 4, 2017
3a07938
get rid of byAttribute method in trip_list category
skwiens Dec 4, 2017
4038f7b
make render errors its own function
skwiens Dec 4, 2017
19e3ee1
clear modals in its own function
skwiens Dec 4, 2017
c8a356b
sorting shows color for field
skwiens Dec 4, 2017
5e132d0
get trip moved to own function
skwiens Dec 4, 2017
d2b9b1f
show reservation form moved to events
skwiens Dec 4, 2017
045e629
reservation form submit function moved to events
skwiens Dec 4, 2017
d69f5ef
document.ready has all logic removed to show functionality of the pro…
skwiens Dec 4, 2017
a403c39
clean up
skwiens Dec 4, 2017
864d25a
display to hundredths in cost
skwiens Dec 4, 2017
dd08cb2
featured trip
skwiens Dec 4, 2017
7b0d77c
dropdown menu for continent
skwiens Dec 4, 2017
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
99 changes: 94 additions & 5 deletions dist/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,108 @@
<html>
<head>
<meta charset="utf-8">
<title>My JavaScript App</title>
<title>Aventura</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/jquery-modal/0.9.1/jquery.modal.min.css" />
</head>
<body>
<header>

<header class="row">
<h1 class="column small-12 large-10">Camino de Aventura</h1>
<button id="newTripBtn" class="button column small-12 large-2">Add a new trip!</button>
</header>
<main>
<main class="row">
<section class="column small-12 large-4">
<article id="trip-description">
</article>
</section>
<section>
<form id="filter-form">
<select class="column small-6 large-3" id="trip-query" name="field">
<option value="name">Name</option>
<option value="category">Category</option>
<option value="continent">Continent</option>
<option value="weeks">Weeks</option>
<option value="cost">Cost</option>
</select>
<div class="column small-6 large-5">
<input id="query-value" type="text" name="filter-req"></input>
</div>
</form>
<table class="column small-12 large-8" summary="Trips that are available through Trek. You can search or sort by priority including category, continent, length of the trip in weeks, or the cost">
<thead>
<th class="sort name">Name</th>
<th class="sort category">Category</th>
<th class="sort continent">Continent</th>
<th class="sort weeks">Weeks</th>
<th class="sort cost">Cost</th>
</thead>
<tbody id="trips-list">
</tbody>
</table>
</section>
<section id="status-messages-modal">
<div class="text-center" id="status-messages">
</div>
</section>
<section id="new-trip-modal">
<form id="add-trip-form">
<h2>Add a New Trip</h2>
<label for="title">Name</label>
<input type="text" name="name"></input>
<label for="category">Category</label>
<input type="text" name="category"></input>
<label for="continent">Continent</label>
<select name="continent">
<option value="Africa">Africa</option>
<option value="Antartica">Antartica</option>
<option value="Asia">Asia</option>
<option value="Australasia">Australasia</option>
<option value="Europe">Europe</option>
<option value="North America">North America</option>
<option value="South America">South America</option>
</select>

<label for="weeks">Weeks</label>
<input type="number" name="weeks" min="0"></input>
<label for="cost">Cost</label>
<input type="number" name="cost" min="0"></input>
<label for="about">About</label>
<input type="text" name="about"></input>
<input type="submit" value="Submit Trip!" class="button"></input>
</form>
</section>
<section id="res-modal">
<form class="form" id="reservation-form">
<label>Name</label>
<input type="text" name="res-name" id="res-name" />
<label>Age</label>
<input type="number" name="res-age" id="res-age"/>
<label>Email</label>
<input type="email" name="res-email" id="res-email"/>
<input id="submit-res" type="submit" value="Make Reservation!" class="button" />
</form>
</section>
</main>
<footer>

</footer>
<script type = "text/template" id="trip-template">
<tr class="trip" data-id="<%- id %>">
<td><%- name %></td>
<td><%- category %></td>
<td><%- continent %></td>
<td><%- weeks %></td>
<td><%- parseFloat(cost).toFixed(2) %></td>
</tr>
</script>
<script type = "text/template" id="trip-details-template">
<h3><%- name %></h3>
<p>Cateogy: <%- category %></p>
<p>Continent: <%- continent %></p>
<p>Length: <%- weeks %> weeks</p>
<p>Cost: $<%- parseFloat(cost).toFixed(2) %></p>
<p class="text-justify"><%- about %></p>
<button class="button" id="res-form-btn" data-id="<%- id %>">Reserve a spot on this trip!</button>
</script>
<script src="app.bundle.js" type="text/javascript"></script>
</body>
</html>
5 changes: 5 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@
"dependencies": {
"backbone": "^1.3.3",
"jquery": "^3.2.1",
"jquery-modal": "^0.9.1",
"underscore": "^1.8.3"
}
}
209 changes: 207 additions & 2 deletions src/app.js
Original file line number Diff line number Diff line change
@@ -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(`<p>${errors[error]}</p>`);
})
$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(`<p>${trip.get('name')} added!</p>`);
$statusMessages.css('display', 'block');
},
failedSave(trip, response) {
renderErrors(response.responseJSON.errors);
trip.destroy();
},
successReservation(reservation) {
$statusMessages.empty();
$statusMessages.append(`<p>${reservation.get('name')} added!</p>`);
$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(`<input type="hidden" name="res-tripID" value="${tripID}">`);
$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('<h1>Hello World!</h1>');
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('<h2> FEATURED TRIP </h2>');
$tripDescription.append(tripDetailsTemplate(trip.attributes));
});

});
12 changes: 12 additions & 0 deletions src/app/collections/trip_list.js
Original file line number Diff line number Diff line change
@@ -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;
30 changes: 30 additions & 0 deletions src/app/models/reservation.js
Original file line number Diff line number Diff line change
@@ -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
Loading