Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Laura Robertson -- Carets #33

Open
wants to merge 16 commits into
base: master
Choose a base branch
from
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
79 changes: 75 additions & 4 deletions dist/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,90 @@
<html>
<head>
<meta charset="utf-8">
<title>My JavaScript App</title>
<title>TrekTrek</title>
<link rel="stylesheet" type="text/css" href="../src/css/foundation.css">
<link rel="stylesheet" type="text/css" href="../src/css/style.css">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="shortcut icon" href="favicon.png">
</head>
<body>
<header>

<h1>BackTREK<span class='float-right button' id='add-trip-button'>Add a trip</span></h1>
</header>
<main>

<main class='row'>

<section id="status-messages">
<button class="clear float-right">
<img src="http://www.pvhc.net/img223/xeiwtpbbgpmyhtcnjvwt.png" alt="Clear">
</button>
<ul></ul>
</section>

<section class="columns large-6 small-12">
<button id='all-trips' class='button'>Trips</button>
<table>
<thead>
<th class="sort name">Name</th>
<th class="sort continent">Continent</th>
<th class="sort weeks">Weeks</th>
<th class="sort cost">Cost</th>
<th class="sort category">Category</th>
</thead>
<tbody id="trip-list">
</tbody>
</table>
</section>

<section class="columns large-6 small-12" id="trip-details"></section>

<div id='add-trip'>
<form class='add-trip-form'>
<h4>Add a trip</h4>
<input type='text' name='name' placeholder='Name' />
<input type='text' name='continent' placeholder='Continent' />

Choose a reason for hiding this comment

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

A select element would make a better choice, not giving the user an option for an invalid continent.

<input type='text' name='about' placeholder='About' />

Choose a reason for hiding this comment

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

A textarea would make a better choice.

<input type='text' name='category' placeholder='Category' />
<input type='number' name='weeks' placeholder='Weeks' />
<input type='text' name='cost' placeholder='Cost' />
<input type="submit" value="Add trip" class="button"></input>
</form>
</div>

</main>
<footer>

<footer>
&copy; 2017
</footer>

<script type="text/template" id="trip-template">
<tr class="trip">
<td><%- name %></td>
<td><%- continent %></td>
<td><%- weeks %> weeks</td>
<td><%- cost %></td>
<td><%- category %></td>
</tr>
</script>

<script type="text/template" id="trip-details-template">
<article class="trip">
<h2><%- name %></h2>
<h5><%- continent %></h5>
<h5><%- weeks %> weeks</h5>
<h5><%- category %></h5>
<p><%- about %></p>
<h3>$ <%- cost %></h3>

<form class="book-trip" action='https://ada-backtrek-api.herokuapp.com/trips/<%- id %>/reservations' method='post' >
<input type='text' name='name' placeholder='Name' />
<input type='text' name='age' placeholder='Age' />
<input type='text' name='email' placeholder='Email' />
<input type="submit" value="Book it" class="button"></input>
</form>
</article>
</script>

<script src="app.bundle.js" type="text/javascript"></script>
</body>
</html>
Binary file removed favicon.ico
Binary file not shown.
147 changes: 145 additions & 2 deletions src/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,155 @@
import $ from 'jquery';
import _ from 'underscore';

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

// CSS
import './css/foundation.css';
import './css/style.css';

console.log('it loaded!');
// ----------- All trips / Details ------------
const tripList = new TripList();
let tripTemplate;
let tripDetails;

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

tripList.forEach((trip) => {
$tripList.append($(tripTemplate(trip.attributes)).attr('id', `${trip.id}`));
});
};

const fields = ['name', 'continent', 'about', 'category', 'weeks', 'cost'];
// const continents = ['Africa', 'Antarctica', 'Asia', 'Australasia', 'Europe', 'South America', 'North America', 'Null'];

// ------------- Status Messages --------------
const updateStatusMessageFrom = (messageHash) => {
$('#status-messages ul').empty();
for(let messageType in messageHash) {
messageHash[messageType].forEach((message) => {
$('#status-messages ul').append($(`<li>${messageType}: ${message}</li>`));
})
}
$('#status-messages').show();
}

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

const events = {
// ------------- Add a new Trip --------------
addTrip(event) {
console.log(this);
event.preventDefault();
const tripData = {};
fields.forEach((field) => {
tripData[field.toLowerCase()] = $(`.add-trip-form input[name=${field}]`).val();
console.log(field);
console.log(tripData[field]);
});

const trip = new Trip(tripData);

if (trip.isValid()) {

trip.save({}, {
success: events.successfullSave,
error: events.failedSave,
});
} else {
updateStatusMessageFrom(trip.validationError);
}

},
successfullSave(trip, response) {
updateStatusMessageWith(`${trip.get('name')} added!`);
tripList.add(trip);
},
failedSave(trip, response) {
updateStatusMessageFrom(response.responseJSON.errors);
trip.destroy();
},
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 (fields.includes(className)) {
console.log(className);
if (className === tripList.comparator) {
tripList.models.reverse();
tripList.trigger('sort', tripList);
}
else {
tripList.comparator = className;
tripList.sort();
}
}
});
},
}

// ----------- All trips / Details ------------
$(document).ready( () => {
$('main').html('<h1>Hello World!</h1>');
tripTemplate = _.template($('#trip-template').html());
tripDetails = _.template($('#trip-details-template').html());
$('.add-trip-form').submit(events.addTrip);
$('.sort').click(events.sortTrips);
tripList.on('sort', render, tripList);

$('#trip-list').hide();
tripList.on('update', render, tripList);
tripList.fetch();

$('#all-trips').on('click', function() {
$('#trip-list').toggle();
});

// ----------- Trip Details -------------
$('#trip-list').on('click', 'tr', function() {
$('tr').css('background', '');
const id = $(this).attr('id');
const url = new Trip(this).url();

$(`#${id}`).css('background','pink');

$.get(url, function(response) {
$(`#trip-details`).html(tripDetails(response));
});
});

// ----------- Modal ------------
$('#add-trip-button').on('click', function() {
$('#add-trip').css('display', 'block');
});

$(document).on('click', function() {
const modal = document.getElementById('add-trip');

Choose a reason for hiding this comment

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

Avoid mixing JavaScript and jQuery selections, use one or the other, in this case I'd suggest jQuery.


if (event.target == modal) {
event.stopPropagation();
modal.style.display = 'none';
}
});

// ------------- Reserve a Trip --------------
$('body').on('submit', '.book-trip', function form(e) {

Choose a reason for hiding this comment

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

validations??

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

$.post(url, formData, () => {
$('.book-trip').html('<h3>Booking Complete</h3>');
}).fail(() => {
$('.book-trip').html('<h3>Booking failed</h3>');
});
});
});
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;
48 changes: 48 additions & 0 deletions src/app/models/trip.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import Backbone from 'backbone';

const Trip = Backbone.Model.extend({
urlRoot: 'https://ada-backtrek-api.herokuapp.com/trips/',
validate: function(attributes) {
const errors = {};

if (!attributes.name) {
errors['name'] = ["Name cannot be blank"];
}

if (!attributes.continent) {
errors['continent'] = ["Continent cannot be blank"];
}

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

if (!attributes.weeks) {
errors['weeks'] = ["Number of weeks cannot be blank"];
}

if ( isNaN( attributes.weeks ) ) {
errors['weeks'] = ["Weeks must be a number"];
} else if ( parseInt(attributes.weeks) < 1 ) {
errors['weeks'] = ["Number of weeks must be greater than than 0"];
}

if (!attributes.cost) {
errors['cost'] = ["Cost cannot be blank"];
}

if ( isNaN( attributes.cost ) ) {
errors['cost'] = ["Cost must be a number"];
} else if ( parseInt(attributes.cost) < 1 ) {
errors['cost'] = ["Cost must be greater than than 0"];
}

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

export default Trip;
73 changes: 72 additions & 1 deletion src/css/style.css
Original file line number Diff line number Diff line change
@@ -1,2 +1,73 @@
#add-trip {
display: none;
position: fixed;
z-index: 999;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: black;
background: rgba(0,0,0,0.5);
}

/* Your styles go here! */
.add-trip-form {
background: white;
position: fixed;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
padding: 30px;
width: 400px;
border-radius: 20px;
}

/* Styles for error section at the top */
#status-messages {
background-color: #dfdfdf;
/* Hide by default */
display: none;
border-radius: 1rem;
margin: 1rem;
height: 100%;
padding: .8rem;
}

#status-messages ul {
list-style: none;
margin-left: 0;
}

#status-messages button.clear {
margin-top: .2rem;
height: 100%;
}

#status-messages button.clear img {
width: 2rem;
}

#status-messages .error {
color: red;
}

#status-messages .success {
color: green;
}

/* Sorting */
th.sort {
cursor: pointer;
}

.current-sort-field {
background-color: darkgrey;
color: white;
}

table tbody td {
padding: 5px;
}

table {
table-layout: fixed;
}