Coder Social home page Coder Social logo

acciobnb's Introduction

Welcome to the Magical World of AccioBnB.

Click here to visit the live site. Accio AccioBnB!

Introduction

AccioBnB is a clone of AirBnB at the time of creation. AirBnB is a home rental service that allows hosts the ability the rent out their proprierties at certain prices and the user can make reservations to stay at their homes. Users are able to make a reservation and leave reviews of their stay. The technologies implemented in this project include:

  • Languages: JavaScript, Ruby, HTML, CSS
  • Frontend and State Management: React, React-Redux, Google Maps JavaScript API
  • Database: PostgreSQL
  • Hosting: Render
  • Asset Storage: AWS Simple Cloud Stoage (S3) with IAM user permissions.

Account Creation

Users are able to explore the site without being signed up. However, they are prompted to sign in when required to do so. Once prompted, users are able to create an account, log into their own account, or login as a demo user. As a demo user, you are able to experience the site's full functionality. Error handling and password protection is included in the sign in. The user receives errors if they do not pass database and validation checks (handled by the Ruby on Rails backend) and displayed on the login page.

The login and account creation forms are modals. The modals are available in all portions of the site and use the application's front-end state to determine whether or not they should be displayed. The "ui" slice of state has its own reducer and keeps track of whether a modal should or should not be shown. The modal component itself also utilizes the React useState hook to keep track of whether it should be shown.

Introduction Gif

const uiReducer = (state = {}, action) => {
	const nextState = {...state};

	switch(action.type) {
		case RECEIVE_SIGNUP_MODAL:
			nextState.signUpModal = action.modalState;
			return nextState
		case RECEIVE_LOGIN_MODAL: 
		nextState.logInModal = action.modalState;
			return nextState;
		case RECEIVE_RESERVATION_MODAL:
			nextState.reservationModal = action.modalState
			return nextState
		case RECEIVE_CREATE_REVIEW_MODAL:
			nextState.createReviewModal = action.modalState
			return nextState;
		default: 
			return state; 
	}
}

Listings

The main landing page of AccioBnB displays the site's listings, including a search bar and details about each listing. Clicking on a listing redirects the user to the individual listing's show page. The listing show page includes pictures of the listing, a description, the host, comments, ratings, Google Maps, and more. It also includes the ability to the make a reservation.

Listing Show

Notably, the ListingShow page houses information from mutliple database sources in order to display the listing, host, and review information. It utilizes useSelector and useParams hooks to grab infomration.

const ListingShow = () => {
 const dispatch = useDispatch();
 const sessionUser = useSelector(sessionAction.sessionUser);
 const { listingId } = useParams(); 
 const listing = useSelector(getListing(listingId));
 const hostId = listing ? listing.hostId : null;
 const host = useSelector(state => state.users ? state.users[hostId] : null);
 const users = useSelector(retrieveUsers);
 const reviews = useSelector(getReviews)

Reservations

Each listing's show page includes the reservation tool. It is one of the more intricate tools in the application, utilized in multiple places, and consists of multiple interconnected React componenets. You are only able to make a reservation if you are logged in. The user inputs the dates in which they would like to stay and the amount of guests that they are bringing. Each host sets the maximum number of guests and the reservation tool does not allow the user to bring more guests than the max amount allowed (as described when creating a listing). Additionally, users are unable to input dates prior to current date. Once the user makes a successful reservation, they are able to see their listing in their own reservations show page. If not logged in, this page will be empty and direct the user to the index. The reservation show page lists details about each reservation and the user is able to click on their reservation which redirects the user back to that listing.

Reservation Tool

Of note, the ReservationPicker component has to receive data from its child DatePicker component (the calendar component). The DatePicker component receives the setter functions of the ReservationPicker's state and utilzes those setter functions to set the state of the check in and check out dates that the user selected. This allows three componenets to pass data and interact dynamically. This includes the ReservationPicker (the parent reservation component), the first-child date selected component, and second-child DatePicker component. As a result, the dates chosen by the user for their stay are displayed in multiple places, dynamically.

const ReservationPicker = ({chooseCheckInDate, chooseCheckOutDate, chooseShowReservationPicker}) => {
	const [checkInDateText, setCheckInDateText] = useState("") 
	const [checkOutDateText, setCheckOutDateText] = useState("") 

	useEffect(() => {
		if (checkInDateText) {
			updateCheckInDateText(checkInDateText);
			chooseCheckInDate(checkInDateText);
		}

		if (checkOutDateText) {
			updateCheckOutDateText(checkOutDateText);
			chooseCheckOutDate(checkOutDateText);
		}
	}, [checkInDateText, checkOutDateText])

	const updateCheckInDateText = (date) => {
		setCheckInDateText(date);
	}

	const updateCheckOutDateText = (date) => {
		setCheckOutDateText(date);
	}

	const closeReservationPicker = () => {
		chooseShowReservationPicker(false);
	}

	return (
		<div id="dp_container">
			<div id="dp_top_options">
				<div id="dp_top_selectdates">
					Select Dates
				</div>
				<div id="dp_top_checkin_checkout">
					<CheckInCheckOut checkInDate={checkInDateText} checkOutDate={checkOutDateText}/>
				</div>
			</div>
			<div id="dp_top_datepicker_wrapper">
				<DatePicker chooseCheckInDate={updateCheckInDateText} chooseCheckOutDate={updateCheckOutDateText} />
			</div>
			<div id="dp_bottom_row_wrapper">
				<div id="dp_close_button" onClick={closeReservationPicker}>Close</div>
			</div>
			<div id="dp_margin_bottom"> </div>
		</div>
	)
}
const DatePicker = ({chooseCheckInDate, chooseCheckOutDate}) => {
	const [selected, setSelected] = useState({}); 
	let checkInDate = "";
	let checkOutDate = "";
	let datePrompt = <div>Please select your check-in date.</div>

Google Maps

The application utlizes Google Maps in two areas: (1) on the initial listings/index page and (2) on each individual listing show page. On the listings/index page, Google Maps is available through the push of a button centered on the bottom of the page. When a user clicks the button, the map is diplayed with a fixed view covering the listings index. The map displays the price of each listing from the listing's index and links to that individual listing's show page. This is exactly what Airbnb does.

Second, on the listing show page, there is an area with a Google Map that shows approximately where the location will be. There is a small easter egg for those who hover over the custom Google Maps InfoWindow, where it displays text stating "Exact Portkey location procided after booking."

const GMap = () => {
	const [gMap, setGMap] = useState();
	const ref = useRef();
	const markers = useRef([]);
	const listings = useSelector(getListings);
	const oval = './whiteoval.png'

	const initialCenterCoords = { lat: 54.11389, lng: -4.59445 };
  const zoomAmount = 6;

	const createTheMap = (latitude, longitude) => {
		const initialMap = new window.google.maps.Map(ref.current, {
			center: { lat: latitude, lng: longitude},
			zoom: zoomAmount,
			gestureHandling:'greedy'
		})
		return initialMap;
	}
	
	useEffect(() => {
		const initialMap = createTheMap(initialCenterCoords.lat, initialCenterCoords.lng); 
		setGMap(initialMap);
	}, []);

	useEffect(() => {
		markers.current = [];
		listings.forEach(listing => {
			const listingLat = Number(listing.lat);
			const listingLng = Number(listing.long);
			const position = {lat: listingLat, lng: listingLng};
			const place = listing.city
			const currentMarker = new window.google.maps.Marker({
				position: position, 
				map: gMap, 
				title: place,
				label: {
					text: '$' + listing.nightlyPrice.toLocaleString(),
					color: 'black',
					fontSize: '15px',
					fontWeight: 'bold'
				},
				icon: {
					url: oval,
					scaledSize: new window.google.maps.Size(80, 38)
				},
				optimized: false,
				url: `/listings/${listing.id}`
			})
			
			window.google.maps.event.addListener(currentMarker, 'click', function() {
				window.location.href = currentMarker.url;
			});
		})
	}, [gMap])

	return (
		<div id="GMap_main_container">
			<div id="map_wrapper">
				<div ref={ref} id="map" />
			</div>
		</div>
	)
}

export default GMap;

Reviews

Users who are not logged in are able to view reviews on each listing's show page, but are unable to make their own reviews. Once logged in, users are able to write reviews of the properties they have "stayed" at. The user can also rate the property, and leave a description of their stay. These reviews contribute to the total rating of the listing. Each review category is also totalled. The user can post their review which is displayed on the listing's show page. If the user is the author, they are able to delete and edit the review. If they are not the author they are unable to delete or edit the review. They are also unable to delete any reviews if they are not logged in.

reviews

const Reviews = ({users}) => {
	const {listingId} = useParams();
	const dispatch = useDispatch();
	const reviews = useSelector(getReviews);
	let content;

	useEffect(() => {
		dispatch(fetchListingReviews(listingId));
	}, [dispatch, listingId])

	if (reviews.length) {
		let pertinentReviews = [];

		reviews.forEach((review) => {
			let reviewAuthor = users.find((user) => user.id === review.userId)
			if (review.listingId === parseInt(listingId)) {
				pertinentReviews.push(<IndividualReview key={review.id} review={review} user={reviewAuthor} />)
			}
		})
		content = pertinentReviews;
	}

	return ( 
		<div id="reviews_container">
			{content}
		</div>
	)
}

Search Bar

Currently, Users are able to filter listings by typing in keywords within the search bar. The search bar is shown on every page and redirets the user to a customized listing index page based on their search term. If the user wants to go back to the main listing's index page, they can click the AccioBnb logo. The search works by parsing the database and identifying keyword matches within the title or description of each listing. It then returns those results in their own search page.

A more customizable search is under construction.

User Profiles

Coming Soon

Additional Features:

Continue Button

The continue button is one of the most reusable components in the application. The component renders its color dynamically based on mouse hover location. It takes two items as props: (1) a clickFunction that is invoked when the component is clicked and (2) textContent that displays the text on the button. This reusable component adds a consistent style throughout the website.

import React, { useState } from "react"
import './ContinueButton.css'

const ContinueButton = ({clickFunction, textContent}) => {
	const [hoverStylePos, setHoverStylePos] = useState({x: 0, y: 0});
	const [isHovering, setIsHovering] = useState(false)
	const [styles, setStyles] = useState({});

	const submitHover = (e) => {
		setIsHovering(true);
		let location = {
			x: e.clientX,
			y: e.clientY
		}
		setHoverStylePos(location);
		setStyles({ 
			backgroundPosition: hoverStylePos.x.toString() + "px , " + hoverStylePos.y.toString() + "px"}
		);
	};

	return (
		<div 
			onClick={clickFunction}
			id={isHovering ? "login_button_hover" : "login_button"} 
			onMouseMove={(e) => submitHover(e)}
			onMouseLeave={() => setIsHovering(false)}
			style={styles}
			> {textContent}
		</div>
	)
}

export default ContinueButton;

acciobnb's People

Contributors

justin-diner avatar

Watchers

 avatar

acciobnb's Issues

Frontend Routes

  • Frontend routes contains wildcard variables written in camelCase
  • Correctly formatted
    • Routes are displayed with inline coding text (backticks)

Sample State

  • State shape is flat!
  • State's keys are camelCased
  • All keys within the values in the state are accessible in the schema
  • Correctly formatted
    • Sample state is rendered with triple backticks, and the language (```javascript...```). This will display the state as a code block instead of a giant line of text
    • Top level slices
      • entities
      • session
      • errors (here or in ui)
      • ui (if needed)
    • Should NOT have nested slices, aka comments inside of posts
      • Some info from other tables is ok, for instance:
        • the author username and imageurl for a post. basically any info that the user can't change
        • like count and a boolean on whether the user likes the post instead of a likes slice

Wiki Home Page

  • Is the first page you see upon entering the wiki
  • Contains a welcome message
  • Contains a link/placeholder for a link to the live page
  • All links in the right sidebar should contain each wiki page and link to the correct page
  • Correctly formatted
    • each wiki page is listed in bullet points
    • all links route the correct page

Database Schema

  • Contains correct datatypes
  • Contains appropriate constraints/details
    • primary key
    • not null
    • unique
    • indexed
    • foreign key
  • Contains bullet points after the table that state which foreign keys will reference to which table, or references to the associations which will be made
    • foreign key and table name are lowercased, snake_cased and back_ticked
  • Correctly formatted
    • schema is written in a table format
    • the table's name are lowercased, snake_cased and back_ticked
    • the table header column names are bolded
    • columns names are lowercased and snaked_cased and back_ticked

Backend Routes

  • Contains the following sections: HTML, API Endpoints(Backend)
  • Each route has a description
  • API Endpoint routes contains wildcard variables written in snake_case
  • Routes does not contain superfluous routes
  • Have API routes that will allow the front end to get all info it needs and does not have unneeded routes:
    • probably doesn't need a GET likes api endpoint because that info comes through the post show

Features List

  • Should have 7 Features.
    • 3 of those are User Auth, Render, and Production README.
    • The other 4 are from the Feature List or they have clarified them with you
  • Contains a description sentence of the app
  • Includes two to three detailed bullets on functionality and presentation of feature
  • At least one CRUD feature, which states what CRUD operations are planned (creation, reading, updating, deletion)
  • Estimates how long it will take the code each Feature
  • Correctly formatted
    • Features are listed in an ordered list
    • Each Feature is broken down into bullet points

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.