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.
As this is a full-stack project, several technologies were used, including:
Stunning visuals using SASS
{
$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 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.
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.
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.
Future Directions
I am excited to continue to work on the site! In future, I plan to implement:
Copyright © 2019 Carolyn Scoville - All Rights Reserved.