About This Project

This is a clone of AirBnb's web app, with a Star Wars theme. Instead of specific countries, users of StarWarsBnb can view and book a stay on a planet from the movies hosted by a character from the movies.

Website

Check out StarWarsBnb

Technologies Used

As this is a full-stack project, several technologies were used, including:

  • React
  • Redux
  • Javascript
  • JSX
  • HTML
  • CSS
  • SASS
  • Ruby
  • Ruby on Rails
  • JBuilder
  • SQL

Notable Features

Stunning visuals using SASS

The challenge

The current version of AirBnb's CSS has seemingly grown without refactoring, as almost every element on the page has a CSS tag of !important attached. For those who are unfamiliar with this tag, it is added when the CSS is not behaving the way you would expect it to, and is kind of a master override for a particular style. For instance, you really want links to be teal, but for some reason they aren't, due to some other property they are inheriting deep in the CSS stylesheets. So, you add !important and your links are magically teal.

I was worried that I would have to make use of this brute-force tag, but I happily did not. I have achieved an uncanny resemblance to AirBnb's site without the use of a single !important tag.

The Solution

 

Using a CSS pre-processor called SASS allowed me to efficiently carry out many actions that you could not normally do in CSS, such as nesting styles, and setting variables that can be used in several different selectors. For instance, I set variables in my SASS files to keep track of AirBnb color themes, and used them throughout several files

{
$airBnbPink: #FF5A5F;
$airBnbTeal: #008489;
$airBnbGray: #484848;
$airBnbLightGray: rgb(120, 120, 120);
$airBnbPurple: rgb(87, 37, 51);
$airBnbBorderColor: #DBDBDB;
$standardFontSize: 19px;
$divider: 1px solid $airBnbBorderColor;


.content {
  width: 100%;

  .index {
    width: 90%;
    margin: 0 auto;

    h2 {
      margin-top: 2em;
      color: $airBnbGray;
      font-weight: 500;
      padding-left: 0.5em;
      margin-bottom: 1em;
    }
  }

The use of nesting styles was also extremely helpful in making sure that styles were not inherited unintentionally. For instance, nesting the h2 selector inside the .index selector meant that only h2 tags inside a tag with a class of index would have the styles shown above. This solution kept my code clean and kept me from having to make use of !important.

 Airbnb home page

Airbnb home page

 StarWarsBnb home page

StarWarsBnb home page

Security

Back-end Authentication

For most site interactions, I implemented both front-end and backend user authentication. On the backend, I separated attributes that users could add once they had signed up from the attributes they would absolutely need to create an account. Essential attributes included a username, password digest, session token, and first name, while inessential attributes included a bio and profile picture. I validated these features on the backend at both the database and model level in Rails.

create_table "users", force: :cascade do |t|
  t.string "email_address", null: false
  t.string "password_digest", null: false
  t.string "session_token", null: false
  t.datetime "created_at", null: false
  t.datetime "updated_at", null: false
  t.string "first_name", null: false
  t.string "planet"
  t.boolean "verified", default: false
  t.text "bio"
  t.string "avatar"
  t.string "phone_number"
  t.boolean "superhost", default: false
  t.index ["email_address"], name: "index_users_on_email_address", unique: true
  t.index ["session_token"], name: "index_users_on_session_token"
end

Relational Databases

Users also had other aspects of the site associated with them. For instance, users could host a planet, but also reserve a stay on a planet in future. On the backend, this required the implementation of relational database tables so that I could create associations between a user and a spot (planet).

class User < ApplicationRecord
  after_initialize :ensure_session_token
  validates :password, length: { minimum: 6, allow_nil: true }
  validates :password_digest, :session_token, presence: true
  validates :email_address, presence: true, uniqueness: true
  attr_reader :password

  has_many :spots,
           class_name: :Spot

  has_many :trips,
           class_name: :Booking,
           primary_key: :id,
           foreign_key: :traveler_id

  has_many :bookings,
           through: :spots,
           source: :bookings

From there, I was able to use JBuilder to dynamically pull information for multiple tables to a single webpage.

json.booking do

  json.extract! @booking, :id, :reservation_code, :check_in, :check_out,
                :total_cost, :num_guests, :parse_arrival_month,
                :parse_depart_month, :parse_arrival_day, :parse_depart_day,
                :parse_time_in, :parse_time_out, :total_days

  json.extract! @booking.spot, :planet, :address, :lock_instructions,
                :directions, :house_manual, :house_rules, :spot_first_photo

  json.extract! @booking.spot.host, :avatar, :first_name, :phone_number

Front-end Authentication

On the front end, I used React Router to protect certain routes on the site. For instance, users could not view a planet page without signing in. This also ensured that in future they could not book a planet without signing in, and could only see trips and leave reviews on their own account.

<div className="content">
  <Switch>
    <AuthRoute path='/signup' component={SignupFormContainer} />
    <AuthRoute path='/login' component={LoginFormContainer} />
    <ProtectedRoute path='/rooms/:roomId' component={SpotShowContainer} />
    <ProtectedRoute path='/trips/:tripId' component={BookingShowContainer} />
    <Route path="/rooms" component={SpotsIndexContainer} />
    <Redirect to='/rooms'/>
  </Switch>
</div>

Result

The result was a dynamic, data-rich website with efficiently compartmentalized state.

starwarsbnb spot detail.png

Future Directions

I am excited to continue to work on the site! In future, I plan to implement:

  • A booking feature that allows users to reserve a stay on a planet, view their upcoming trips, update reservations, and cancel reservations
  • A search feature in combination with the Google Maps API that allows users to search listings by date availability
  • A review feature that allows users to leave a review for a trip they took
  • General usability updates such as modal functionality