Airbnb clone, adding a new house

This post is part of a new series where we build a clone of Airbnb with Next.js. See the first post here.

In this lesson I want to add the ability to add a new house.

In the previous lesson I added a link to the /host/new URL, when we don’t have houses yet.

I am going to also add the link to the components/Header.js file, to make it easy to find, even though in a real app you’d move it somewhere less prominent, I think, as it’s not a functionality that’s used many times:

components/Header.js

<li>
  <Link href='/host/new'>
    <a>Add House</a>
  </Link>
</li>

Now create a new file pages/host/new.js

In this file we’re going to create a form to add a new house.

We start, by adding one house field, title, and we we submit the form to the server, on the host/new server-side route:

import { useState } from 'react'
import Head from 'next/head'

import Layout from '../../components/Layout'

const NewHouse = () => {
  const [title, setTitle] = useState('')

  return (
    <Layout
      content={
        <div>
          <Head>
            <title>Add a new house</title>
          </Head>

          <form
            onSubmit={async (event) => {
              event.preventDefault()
              try {
                const response = await axios.post('/api/host/new', {
                  house: {
                    title,
                  },
                })
                if (response.data.status === 'error') {
                  alert(response.data.message)
                  return
                }

                console.log(response)
                goto('/host')
              } catch (error) {
                alert(error.response.data.message)
                return
              }
            }}
          >
            <input
              id='title'
              type='text'
              placeholder='House title'
              onChange={(event) => setTitle(event.target.value)}
            />
            <button>Add house</button>
          </form>

          <style jsx>{``}</style>
        </div>
      }
    />
  )
}

export default NewHouse

We use hooks to store the state of each item in our form.

Now create a new endpoint POST /api/host/new in the server.js file, that uses the Sequelize model to add the house to the database:

server.post('/api/host/new', async (req, res) => {
  const houseData = req.body.house

  if (!req.session.passport) {
    res.writeHead(403, {
      'Content-Type': 'application/json',
    })
    res.end(
      JSON.stringify({
        status: 'error',
        message: 'Unauthorized',
      })
    )

    return
  }

  const userEmail = req.session.passport.user
  User.findOne({ where: { email: userEmail } }).then((user) => {
    houseData.host = user.id
    House.create(houseData).then(() => {
      res.writeHead(200, {
        'Content-Type': 'application/json',
      })
      res.end(JSON.stringify({ status: 'success', message: 'ok' }))
    })
  })
})

I basically check if the user is logged in, and then add its id to the house data, before storing it.

If you try it now, in the terminal you’ll get a long list of errors from Sequelize:

Unhandled rejection SequelizeValidationError: notNull Violation: house.picture cannot be null,
notNull Violation: house.type cannot be null,
notNull Violation: house.town cannot be null,
notNull Violation: house.price cannot be null,
notNull Violation: house.superhost cannot be null,
notNull Violation: house.guests cannot be null,
notNull Violation: house.bedrooms cannot be null,
notNull Violation: house.beds cannot be null,
notNull Violation: house.baths cannot be null,
notNull Violation: house.wifi cannot be null,
notNull Violation: house.kitchen cannot be null,
notNull Violation: house.heating cannot be null,
notNull Violation: house.freeParking cannot be null,
notNull Violation: house.entirePlace cannot be null
//...

because we set those fields to be NOT NULL in the database.

Now that we have a basic form set up, let’s add all the fields we need to avoid this error!

import { useState } from 'react'
import Head from 'next/head'
import axios from 'axios'
import Router from 'next/router'

import Layout from '../../components/Layout'

const NewHouse = () => {
  const [title, setTitle] = useState('')
  const [town, setTown] = useState('')
  const [price, setPrice] = useState(0)
  const [picture, setPicture] = useState('')
  const [description, setDescription] = useState('')
  const [guests, setGuests] = useState(0)
  const [bedrooms, setBedrooms] = useState(0)
  const [beds, setBeds] = useState(0)
  const [baths, setBaths] = useState(0)
  const [wifi, setWifi] = useState(false)
  const [kitchen, setKitchen] = useState(false)
  const [heating, setHeating] = useState(false)
  const [freeParking, setFreeParking] = useState(false)
  const [entirePlace, setEntirePlace] = useState(false)
  const [type, setType] = useState('Entire house')

  const houseTypes = ['Entire house', 'Room']

  return (
    <Layout
      content={
        <div>
          <Head>
            <title>Add a new house</title>
          </Head>

          <form
            onSubmit={async (event) => {
              event.preventDefault()
              try {
                const response = await axios.post('/api/host/new', {
                  house: {
                    title,
                    town,
                    price,
                    picture,
                    description,
                    guests,
                    bedrooms,
                    beds,
                    baths,
                    wifi,
                    kitchen,
                    heating,
                    freeParking,
                    entirePlace,
                    type,
                  },
                })
                if (response.data.status === 'error') {
                  alert(response.data.message)
                  return
                }

                Router.push('/host')
              } catch (error) {
                alert(error.response.data.message)
                return
              }
            }}
          >
            <p>
              <label>House title</label>
              <input
                required
                onChange={(event) => setTitle(event.target.value)}
                type='text'
                placeholder='House title'
              />
            </p>
            <p>
              <label>Town</label>
              <input
                required
                onChange={(event) => setTown(event.target.value)}
                type='text'
                placeholder='Town'
              />
            </p>
            <p>
              <label>Price per night</label>
              <input
                required
                onChange={(event) => setPrice(event.target.value)}
                type='number'
                placeholder='Price per night'
                value={price}
              />
            </p>
            <p>
              <label>House picture URL</label>
              <input
                required
                onChange={(event) => setPicture(event.target.value)}
                type='text'
                placeholder='House picture URL'
              />
            </p>
            <p>
              <label>House description</label>
              <textarea
                required
                onChange={(event) => setDescription(event.target.value)}
              ></textarea>
            </p>

            <div>
              <div>
                <p>
                  <label>Number of guests</label>
                  <input
                    required
                    onChange={(event) => setGuests(event.target.value)}
                    type='number'
                    placeholder='Number of guests'
                    value={guests}
                  />
                </p>
                <p>
                  <label>Number of bedrooms</label>
                  <input
                    required
                    onChange={(event) => setBedrooms(event.target.value)}
                    type='number'
                    placeholder='Number of bedrooms'
                    value={bedrooms}
                  />
                </p>
                <p>
                  <label>Number of beds</label>
                  <input
                    required
                    onChange={(event) => setBeds(event.target.value)}
                    type='number'
                    placeholder='Number of beds'
                    value={beds}
                  />
                </p>
                <p>
                  <label>Number of baths</label>
                  <input
                    required
                    onChange={(event) => setBaths(event.target.value)}
                    type='number'
                    placeholder='Number of baths'
                    value={baths}
                  />
                </p>
              </div>

              <div>
                <p>
                  <label>Does it have Wifi?</label>
                  <select
                    onChange={(event) => setWifi(event.target.value)}
                    value={wifi}
                  >
                    <option value='true'>Yes</option>
                    <option value='false'>No</option>
                  </select>
                </p>
                <p>
                  <label>Does it have a kitchen?</label>
                  <select
                    onChange={(event) => setKitchen(event.target.value)}
                    value={kitchen}
                  >
                    <option value='true'>Yes</option>
                    <option value='false'>No</option>
                  </select>
                </p>
                <p>
                  <label>Does it have heating?</label>
                  <select
                    onChange={(event) => setHeating(event.target.value)}
                    value={heating}
                  >
                    <option value='true'>Yes</option>
                    <option value='false'>No</option>
                  </select>
                </p>
                <p>
                  <label>Does it have free parking?</label>
                  <select
                    onChange={(event) => setFreeParking(event.target.value)}
                    value={freeParking}
                  >
                    <option value='true'>Yes</option>
                    <option value='false'>No</option>
                  </select>
                </p>
                <p>
                  <label>Is it the entire place?</label>
                  <select
                    onChange={(event) => setEntirePlace(event.target.value)}
                    value={entirePlace}
                  >
                    <option value='true'>Yes</option>
                    <option value='false'>No</option>
                  </select>
                </p>
                <p>
                  <label>Type of house</label>
                  <select
                    onChange={(event) => setType(event.target.value)}
                    value={type}
                  >
                    {houseTypes.map((item, key) => (
                      <option value={item} key={key}>
                        {item}
                      </option>
                    ))}
                  </select>
                </p>
              </div>
            </div>

            <button>Add house</button>
          </form>

          <style jsx>{`
            input[type='number'],
            select,
            textarea {
              display: block;
              padding: 20px;
              font-size: 20px !important;
              width: 100%;
              border: 1px solid #ccc;
              border-radius: 4px;
              box-sizing: border-box;
              margin-bottom: 10px;
            }
          `}</style>
        </div>
      }
    />
  )
}

export default NewHouse

I imported the Next.js Router:

import Router from 'next/router'

so that after the form is successfully submitted, we can call Router.push('/host') to redirect the user to the list of houses.

I used the houseTypes array in one select to show you how to dynamically add data to a form.

The house picture is a path, either an absolute URL or relative to the base path.

I also set all the fields as required, frontend-side, so we get automatic validation by the browser.

Great! The form should show up nicely now.


This content originally appeared on flaviocopes.com and was authored by flaviocopes.com

This post is part of a new series where we build a clone of Airbnb with Next.js. See the first post here.

In this lesson I want to add the ability to add a new house.

In the previous lesson I added a link to the /host/new URL, when we don’t have houses yet.

I am going to also add the link to the components/Header.js file, to make it easy to find, even though in a real app you’d move it somewhere less prominent, I think, as it’s not a functionality that’s used many times:

components/Header.js

<li>
  <Link href='/host/new'>
    <a>Add House</a>
  </Link>
</li>

Now create a new file pages/host/new.js

In this file we’re going to create a form to add a new house.

We start, by adding one house field, title, and we we submit the form to the server, on the host/new server-side route:

import { useState } from 'react'
import Head from 'next/head'

import Layout from '../../components/Layout'

const NewHouse = () => {
  const [title, setTitle] = useState('')

  return (
    <Layout
      content={
        <div>
          <Head>
            <title>Add a new house</title>
          </Head>

          <form
            onSubmit={async (event) => {
              event.preventDefault()
              try {
                const response = await axios.post('/api/host/new', {
                  house: {
                    title,
                  },
                })
                if (response.data.status === 'error') {
                  alert(response.data.message)
                  return
                }

                console.log(response)
                goto('/host')
              } catch (error) {
                alert(error.response.data.message)
                return
              }
            }}
          >
            <input
              id='title'
              type='text'
              placeholder='House title'
              onChange={(event) => setTitle(event.target.value)}
            />
            <button>Add house</button>
          </form>

          <style jsx>{``}</style>
        </div>
      }
    />
  )
}

export default NewHouse

We use hooks to store the state of each item in our form.

Now create a new endpoint POST /api/host/new in the server.js file, that uses the Sequelize model to add the house to the database:

server.post('/api/host/new', async (req, res) => {
  const houseData = req.body.house

  if (!req.session.passport) {
    res.writeHead(403, {
      'Content-Type': 'application/json',
    })
    res.end(
      JSON.stringify({
        status: 'error',
        message: 'Unauthorized',
      })
    )

    return
  }

  const userEmail = req.session.passport.user
  User.findOne({ where: { email: userEmail } }).then((user) => {
    houseData.host = user.id
    House.create(houseData).then(() => {
      res.writeHead(200, {
        'Content-Type': 'application/json',
      })
      res.end(JSON.stringify({ status: 'success', message: 'ok' }))
    })
  })
})

I basically check if the user is logged in, and then add its id to the house data, before storing it.

If you try it now, in the terminal you’ll get a long list of errors from Sequelize:

Unhandled rejection SequelizeValidationError: notNull Violation: house.picture cannot be null,
notNull Violation: house.type cannot be null,
notNull Violation: house.town cannot be null,
notNull Violation: house.price cannot be null,
notNull Violation: house.superhost cannot be null,
notNull Violation: house.guests cannot be null,
notNull Violation: house.bedrooms cannot be null,
notNull Violation: house.beds cannot be null,
notNull Violation: house.baths cannot be null,
notNull Violation: house.wifi cannot be null,
notNull Violation: house.kitchen cannot be null,
notNull Violation: house.heating cannot be null,
notNull Violation: house.freeParking cannot be null,
notNull Violation: house.entirePlace cannot be null
//...

because we set those fields to be NOT NULL in the database.

Now that we have a basic form set up, let’s add all the fields we need to avoid this error!

import { useState } from 'react'
import Head from 'next/head'
import axios from 'axios'
import Router from 'next/router'

import Layout from '../../components/Layout'

const NewHouse = () => {
  const [title, setTitle] = useState('')
  const [town, setTown] = useState('')
  const [price, setPrice] = useState(0)
  const [picture, setPicture] = useState('')
  const [description, setDescription] = useState('')
  const [guests, setGuests] = useState(0)
  const [bedrooms, setBedrooms] = useState(0)
  const [beds, setBeds] = useState(0)
  const [baths, setBaths] = useState(0)
  const [wifi, setWifi] = useState(false)
  const [kitchen, setKitchen] = useState(false)
  const [heating, setHeating] = useState(false)
  const [freeParking, setFreeParking] = useState(false)
  const [entirePlace, setEntirePlace] = useState(false)
  const [type, setType] = useState('Entire house')

  const houseTypes = ['Entire house', 'Room']

  return (
    <Layout
      content={
        <div>
          <Head>
            <title>Add a new house</title>
          </Head>

          <form
            onSubmit={async (event) => {
              event.preventDefault()
              try {
                const response = await axios.post('/api/host/new', {
                  house: {
                    title,
                    town,
                    price,
                    picture,
                    description,
                    guests,
                    bedrooms,
                    beds,
                    baths,
                    wifi,
                    kitchen,
                    heating,
                    freeParking,
                    entirePlace,
                    type,
                  },
                })
                if (response.data.status === 'error') {
                  alert(response.data.message)
                  return
                }

                Router.push('/host')
              } catch (error) {
                alert(error.response.data.message)
                return
              }
            }}
          >
            <p>
              <label>House title</label>
              <input
                required
                onChange={(event) => setTitle(event.target.value)}
                type='text'
                placeholder='House title'
              />
            </p>
            <p>
              <label>Town</label>
              <input
                required
                onChange={(event) => setTown(event.target.value)}
                type='text'
                placeholder='Town'
              />
            </p>
            <p>
              <label>Price per night</label>
              <input
                required
                onChange={(event) => setPrice(event.target.value)}
                type='number'
                placeholder='Price per night'
                value={price}
              />
            </p>
            <p>
              <label>House picture URL</label>
              <input
                required
                onChange={(event) => setPicture(event.target.value)}
                type='text'
                placeholder='House picture URL'
              />
            </p>
            <p>
              <label>House description</label>
              <textarea
                required
                onChange={(event) => setDescription(event.target.value)}
              ></textarea>
            </p>

            <div>
              <div>
                <p>
                  <label>Number of guests</label>
                  <input
                    required
                    onChange={(event) => setGuests(event.target.value)}
                    type='number'
                    placeholder='Number of guests'
                    value={guests}
                  />
                </p>
                <p>
                  <label>Number of bedrooms</label>
                  <input
                    required
                    onChange={(event) => setBedrooms(event.target.value)}
                    type='number'
                    placeholder='Number of bedrooms'
                    value={bedrooms}
                  />
                </p>
                <p>
                  <label>Number of beds</label>
                  <input
                    required
                    onChange={(event) => setBeds(event.target.value)}
                    type='number'
                    placeholder='Number of beds'
                    value={beds}
                  />
                </p>
                <p>
                  <label>Number of baths</label>
                  <input
                    required
                    onChange={(event) => setBaths(event.target.value)}
                    type='number'
                    placeholder='Number of baths'
                    value={baths}
                  />
                </p>
              </div>

              <div>
                <p>
                  <label>Does it have Wifi?</label>
                  <select
                    onChange={(event) => setWifi(event.target.value)}
                    value={wifi}
                  >
                    <option value='true'>Yes</option>
                    <option value='false'>No</option>
                  </select>
                </p>
                <p>
                  <label>Does it have a kitchen?</label>
                  <select
                    onChange={(event) => setKitchen(event.target.value)}
                    value={kitchen}
                  >
                    <option value='true'>Yes</option>
                    <option value='false'>No</option>
                  </select>
                </p>
                <p>
                  <label>Does it have heating?</label>
                  <select
                    onChange={(event) => setHeating(event.target.value)}
                    value={heating}
                  >
                    <option value='true'>Yes</option>
                    <option value='false'>No</option>
                  </select>
                </p>
                <p>
                  <label>Does it have free parking?</label>
                  <select
                    onChange={(event) => setFreeParking(event.target.value)}
                    value={freeParking}
                  >
                    <option value='true'>Yes</option>
                    <option value='false'>No</option>
                  </select>
                </p>
                <p>
                  <label>Is it the entire place?</label>
                  <select
                    onChange={(event) => setEntirePlace(event.target.value)}
                    value={entirePlace}
                  >
                    <option value='true'>Yes</option>
                    <option value='false'>No</option>
                  </select>
                </p>
                <p>
                  <label>Type of house</label>
                  <select
                    onChange={(event) => setType(event.target.value)}
                    value={type}
                  >
                    {houseTypes.map((item, key) => (
                      <option value={item} key={key}>
                        {item}
                      </option>
                    ))}
                  </select>
                </p>
              </div>
            </div>

            <button>Add house</button>
          </form>

          <style jsx>{`
            input[type='number'],
            select,
            textarea {
              display: block;
              padding: 20px;
              font-size: 20px !important;
              width: 100%;
              border: 1px solid #ccc;
              border-radius: 4px;
              box-sizing: border-box;
              margin-bottom: 10px;
            }
          `}</style>
        </div>
      }
    />
  )
}

export default NewHouse

I imported the Next.js Router:

import Router from 'next/router'

so that after the form is successfully submitted, we can call Router.push('/host') to redirect the user to the list of houses.

I used the houseTypes array in one select to show you how to dynamically add data to a form.

The house picture is a path, either an absolute URL or relative to the base path.

I also set all the fields as required, frontend-side, so we get automatic validation by the browser.

Great! The form should show up nicely now.


This content originally appeared on flaviocopes.com and was authored by flaviocopes.com


Print Share Comment Cite Upload Translate Updates
APA

flaviocopes.com | Sciencx (2022-01-06T05:00:00+00:00) Airbnb clone, adding a new house. Retrieved from https://www.scien.cx/2022/01/06/airbnb-clone-adding-a-new-house/

MLA
" » Airbnb clone, adding a new house." flaviocopes.com | Sciencx - Thursday January 6, 2022, https://www.scien.cx/2022/01/06/airbnb-clone-adding-a-new-house/
HARVARD
flaviocopes.com | Sciencx Thursday January 6, 2022 » Airbnb clone, adding a new house., viewed ,<https://www.scien.cx/2022/01/06/airbnb-clone-adding-a-new-house/>
VANCOUVER
flaviocopes.com | Sciencx - » Airbnb clone, adding a new house. [Internet]. [Accessed ]. Available from: https://www.scien.cx/2022/01/06/airbnb-clone-adding-a-new-house/
CHICAGO
" » Airbnb clone, adding a new house." flaviocopes.com | Sciencx - Accessed . https://www.scien.cx/2022/01/06/airbnb-clone-adding-a-new-house/
IEEE
" » Airbnb clone, adding a new house." flaviocopes.com | Sciencx [Online]. Available: https://www.scien.cx/2022/01/06/airbnb-clone-adding-a-new-house/. [Accessed: ]
rf:citation
» Airbnb clone, adding a new house | flaviocopes.com | Sciencx | https://www.scien.cx/2022/01/06/airbnb-clone-adding-a-new-house/ |

Please log in to upload a file.




There are no updates yet.
Click the Upload button above to add an update.

You must be logged in to translate posts. Please log in or register.