Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
103 changes: 100 additions & 3 deletions dist/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,116 @@
<html>
<head>
<meta charset="utf-8">
<title>My JavaScript App</title>
<title>Trek!</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link href="https://fonts.googleapis.com/css?family=Dosis|Indie+Flower" rel="stylesheet">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/foundation/6.2.4/foundation.min.css">
<link rel="stylesheet" href="styles.css">
</head>
<body>
<header>

<h1>Trek!</h1>
<h3>Adventure awaits...</h3>
<button type="button" id="search">Find Trips</button>
<button type="button" id="add">Add a New Trip</button>
</header>
<main>
<div id="status-messages"><ul></ul></div>
<div id="add-trip" class="modal">
<div class="modal-content">
<span class="close">&times;</span>
<form id="new-trip" method="post">
<h3>Add a Trip</h3>
<label for="name">Name</label>
<input type="text" id="name" name="name"></input>
<label for="continent">Continent</label>
<input type="text" id="continent" name="continent"></input>
<label for="weeks">Weeks</label>
<input type="text" id="weeks" name="weeks"></input>
<label for="category">Category</span></label>
<input type="text" id="category" name="category"></input>
<label for="cost">Cost</label>
<input type="text" id="cost" name="cost"></input>
<label for="about">Description</label>
<input type="text" id="about" name="label"></input>
<button type="submit">Add Trip</button>
</form>
</div>
</div>
<article id="trips" class="small-12 large-6 columns">
<form id="filters">
<select>
<option value="null">Search By...</option>
<option value="name">Name</option>
<option value="continent">Continent</option>
<option value="weeks">Weeks</option>
<option value="category">Category</option>
<option value="cost">Cost</option>
</select>
<input type="text"></input>
</form>
<table>
<caption>Trips</caption>
<thead>
<th class="sort name">Name</th>
<th class="sort continent">Continent</th>
<th class="sort weeks">Weeks</th>
<th class="sort category">Category</th>
<th class="sort cost">Cost</th>
</thead>
<tbody id="trip-list">
</tbody>
</table>
</article>
<aside id="trip" class="small-12 large-5 columns"></aside>

</main>
<footer>

&copy; 2017 LAC
</footer>
<script src="app.bundle.js" type="text/javascript"></script>
<script type="text/template" id="trip-template">
<tr class="trip" data-id="<%- id %>">
<td class="name">
<%- name %>
</td>
<td class="continent">
<%- continent %>
</td>
<td class="weeks">
<%- weeks %>
</td>
<td class="category">
<%- category %>
</td>
<td class="cost">
$<%- cost.toFixed(2) %>
</td>
</tr>
</script>
<script type="text/template" id="trip-details-template">
<h3 data-id="<%- id %>"><%- name %></h3>
<p>Trip ID: <%- id %></p>
<p>Destination: <%- continent %></p>
<p>Weeks: <%- weeks %></p>
<p>Cost: $<%- cost.toFixed(2) %></p>
<p>Category: <%- category %></p>
<h4>About</h4>
<p><%- about %></p>
<button id="reserve">Reserve Your Spot Now!</button>
<section id="message"></section>
<form id="reservation" action="https://ada-backtrek-api.herokuapp.com/trips/<%- id %>/reservations" method="post">
<label for="name">Name</label>
<input type="text" id="cust-name" name="name"></input>
<label for="email">Email</label>
<input type="text" id="email" name="email"></input>
<button type="submit">Reserve</button>
</form>
</script>
<!-- <script type="text/template" id="status-message-template">
<li class="<%- status %>">
<%- message %>
</li>
</script> -->
</body>
</html>
Binary file added images/crater_lake.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
195 changes: 194 additions & 1 deletion src/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,201 @@ import _ from 'underscore';
import './css/foundation.css';
import './css/style.css';

import Trip from './app/models/trip';
import TripList from './app/collections/trip_list';

const TRIP_FIELDS = ['name', 'continent', 'category', 'weeks', 'cost'];

let tripList;
let tripTemplate;
let tripDetailsTemplate;

console.log('it loaded!');

const render = function render(tripList) {
const tripListElement = $('#trip-list');
tripListElement.empty();

tripList.forEach((trip) => {
tripListElement.append(tripTemplate(trip.attributes));
});
};

const failLoad = function failLoad(model, response) {
updateStatusMessageFrom(response.responseJSON.errors);
};

const loadTrips = function loadTrips() {
tripList.fetch();
tripList.on('update', render, tripList);
$('#trips').show();
};

const sortTrips = function sortTrips(event) {
$('.current-sort-field').removeClass('current-sort-field');
$(this).addClass('current-sort-field');

const classes = $(this).attr('class').split(/\s+/);

classes.forEach((className) => {
if (TRIP_FIELDS.includes(className)) {
if (className === tripList.comparator) {
tripList.models.reverse();
tripList.trigger('sort', tripList);
} else {
tripList.comparator = className;
tripList.sort();
}
}
});
};

const addTrip = function addTrip(event) {
event.preventDefault();

const tripData = readForm();
const trip = new Trip(tripData);
if (trip.isValid()) {
trip.save({}, {
success: successfulSave,
error: failedSave,
});
} else {
updateStatusMessageFrom(trip.validationError);
}
};

const readForm = function readForm() {
const tripData = {};
TRIP_FIELDS.forEach((field) => {
const inputElement = $(`#new-trip input[name="${ field }"]`);
tripData[field] = inputElement.val();
});
return tripData;
};

const clearForm = function clearForm() {
$('#new-trip input[name]').val('');
};

const clearFilter = function clearFilter() {
$('#filters input').val('');
$('#filters option').prop('selected', function() {
return this.defaultSelected;
});
};

const updateStatusMessageFrom = (messageHash) => {
$('span.error').empty();
for (let messageType in messageHash) {
messageHash[messageType].forEach((message) => {
$(`#new-trip label[for="${ messageType }"]`).append(` <span>${ message }</span>`);
});
}
};

const updateStatusMessagesWith = (message) => {
$('#status-messages ul').empty();
$('#status-messages ul').append(`<li>${ message }</li>`);
$('#status-messages').show();
};

const successfulSave = function(trip, response) {
updateStatusMessagesWith(`${trip.get('name')} added!`)
clearForm();
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is where you should have the tripList refresh, or fetch the trip's name and manually add it to the list.

$('#add-trip').css("display", "none");
tripList.fetch();
};

const failedSave = function(trip, response) {
updateStatusMessageFrom(response.responseJSON.errors);
trip.destroy();
};

const loadTrip = function loadTrip(trip) {
$('#trip').empty();
$('#trip').addClass('green-border');
$('#trip').append(tripDetailsTemplate(trip.attributes));
$('#reservation').hide();
$('#trip').on('click', 'button', function() {
$('#reservation').slideToggle();
});

$('#reservation').submit(function(e) {
e.preventDefault();

const url = $(this).attr('action')
const formData = $(this).serialize();

if ($('#cust-name').val() === '' || $('#email').val() === '') {
$('#reservation span').remove();
$('#reservation').slideToggle();
if ($('#cust-name').val() === '') {
console.log('appending name error');
console.log($('#name').val());
$(`#reservation label[for="name"]`).append(` <span>Cannot be blank</span>`);
}
if ($('#email').val() === '') {
console.log('appending email error');
$(`#reservation label[for="email"]`).append(` <span>Cannot be blank</span>`);
}
console.log('before hide');
} else {
$.post(url, formData, (response) => {
$('#message').html('<p>Spot reserved!</p>');
$('#reservation').hide();
console.log('after hide');
clearForm();
}).fail(() => {
$('#message').html('<p>Reservation failed to save.</p>');
});
}
});
};

$(document).ready( () => {
$('main').html('<h1>Hello World!</h1>');
$('#trips').hide();
clearFilter();
tripTemplate = _.template($('#trip-template').html());
tripDetailsTemplate = _.template($('#trip-details-template').html());

tripList = new TripList();

tripList.on('update', render, tripList);
tripList.on('sort', render, tripList);

$('button#search').on('click', loadTrips);
$('#filters input').on('keyup', function() {
const query = $(this).val().toLowerCase();
const filterCategory = $('#filters select').val().toLowerCase();
const filteredList = tripList.filterBy(filterCategory, query);
if (filteredList.length === 0) {
$('#trips').append('<p>No trips match your search</p>');
} else {
render(filteredList);
}
});

$('.sort').click(sortTrips);
$('#new-trip').on('submit', addTrip);

$('#trip-list').on('click', 'tr', function() {
const trip = tripList.get($(this).attr('data-id'));
trip.fetch({
success: loadTrip,
error: failLoad,
});
});

$('button#add').click(function() {
$('#add-trip').css("display", "block");
});
$('span.close').click(function() {
$('#add-trip').css("display", "none");
});
$('body').click(function(event) {
if (event.target === $('body')) {
$('#add-trip').css("display", "none");
}
});
});
22 changes: 22 additions & 0 deletions src/app/collections/trip_list.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
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;
},
filterBy: function(filterCategory, query) {
if (query === '') {
return this;
}
if (filterCategory === 'weeks' || filterCategory === 'cost') {
return this.where(trip => trip.get(filterCategory) <= Number(query));
} else {
return this.where((trip) => trip.get(filterCategory).toLowerCase().includes(query));
}
},
});

export default TripList;
46 changes: 46 additions & 0 deletions src/app/models/trip.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import Backbone from 'backbone';

const Trip = Backbone.Model.extend({
urlRoot: 'https://ada-backtrek-api.herokuapp.com/trips/',
validate: function(attributes) {
const errors = {};
const CONTINENTS = ['Africa', 'Antartica', 'Asia', 'Australasia', 'Europe', 'North America', 'South America'];
if (!attributes.name) {
errors['name'] = ['Cannot be blank'];
}

if (!attributes.continent) {
errors['continent'] = ['Cannot be blank'];
} else if (!CONTINENTS.includes(attributes.continent)) {
errors['continent'] = ['Must be a valid continent'];
}

if (!attributes.category) {
errors['category'] = ['Cannot be blank'];
}

if (!attributes.weeks) {
errors['weeks'] = ['Cannot be blank'];
} else if (isNaN(attributes.weeks)) {
errors['weeks'] = ['Must be a number'];
} else if (attributes.weeks < 1) {
errors['weeks'] = ['Must be greater than 0'];
}

if (!attributes.cost) {
errors['cost'] = ['Cannot be blank'];
} else if (isNaN(attributes.cost)) {
errors['cost'] = ['Must be a number'];
} else if (attributes.cost <= 0) {
errors['cost'] = ['Must be greater than 0'];
}

if (Object.keys(errors).length > 0) {
return errors;
} else {
return false;
}
}
});

export default Trip;
Loading