AvailableFlatSurface is a clone of OpenTable using a React + Redux front end calling to a Rails + PostgreSQL backend.
CRUD features include reservations, reviews, and saved restaurants.
- Ruby on Rails backend using a normalized PostgreSQL database, RESTful routes, and JBuilder
- React + Redux front end with SASS styling
- Secure photo hosting on Amazon Web Services S3 with Rails Active Storage
- Compiled to ES5 using Webpack and Babel.js
One of the biggest functional challenges was gathering all necessary data to make a reservation upon clicking the time buttons on the search page. Hello Redux! I tracked the party size and dateTime of a reservation using the ui slice of state.
// UI REDUCER
Object.freeze(oldState);
let newState = Object.assign({}, oldState);
switch (action.type) {
case RECEIVE_RESERVATION_FORM_CHANGE:
newState.reservationForm = action.reservationData
return newState
// RESERVATION BUTTON CONTAINER
const mapStateToProps = ({ ui, session }) => ({
searchedDateTime: ui.reservationForm.resDateTime || null,
numPeople: ui.reservationForm.numPeople,
userId: (session.currentUser === null) ? null : session.currentUser.id
});
// RESERVATION BUTTON COMPONENT
handleReservation(e) {
e.preventDefault();
if (this.props.userId === null) return this.props.history.push(`/search/signin`);
let reservation = {
reservation: (this.props.searchedDateTime.getTime()) / 1000, // divide by 1000 for rails
num_people: this.props.numPeople,
user_id: this.props.userId,
restaurant_id: this.props.restaurantId // threaded in from above
};
this.props.makeReservation(reservation)
.then(this.props.history.push(`/profile/${this.props.userId}/reservations`));
}
// RESERVATION BUTTON RENDER
<Link onClick={this.handleReservation}
key={i}
className="submit-button res-submit-button"
to={`/profile/${this.props.userId}/reservations`}>{buttonTime}
</Link>
Early in the project, when I was thinking about how to implement OpenTable's heavy use of icons in a clean, dynamic way, I had one of those Beautiful Mind/orchestral background music moments when React really started to make sense to me. Below, you can see how I built and then mapped over a nested array library to make the restaurant page sidebar.
AvailableFlatSurface | OpenTable |
---|---|
function sidebarDataArr(rest) {
let details = [
['Address', rest.address, 'fas fa-map-marker-alt'],
['Cross Street', rest.crossStreet, 'fas fa-car-alt'],
['Neighborhood', rest.neighborhood, 'far fa-building'],
['Hours', rest.hours, 'far fa-clock'],
['Cuisine', rest.cuisine, 'fas fa-utensils'],
['Dress Code', rest.dressCode, 'fas fa-tshirt'],
['Parking Details', rest.parkingDetails, 'fas fa-parking'],
['Payment Options', rest.paymentOptions, 'fas fa-credit-card'],
['Phone Number', rest.phone, 'fas fa-phone'],
['Website', rest.website, 'far fa-share-square'],
]
return details.map((detail, i) => {
if (!details[1]) return null;
let label = detail[0];
let val = detail[1] || 'N/A';
let icon = detail[2];
val = (label === 'Website' && val !== 'N/A') ? <a href={val}>{val}</a> : val
return (
<li key={i}>
<i className={icon} />
<div>
<label>{label}</label>
<p>{val}</p>
</div>
</li>
)
})
}
This project sought to closely replicate the design of OpenTable. At times (see below), one could argue that AvailableFlatSurface has a more even, aesthetically pleasing arrangement of elements.
AvailableFlatSurface | OpenTable |
---|---|