Open In App

Developing a Budget Travel Planner using React and Interactive maps (Mapbox)

Last Updated : 08 Apr, 2024
Improve
Improve
Like Article
Like
Save
Share
Report

The Budget Travel Planner app is an app that aids users in planning and managing their trips within a specified budget. It helps users curate their bookings of flights, hotels, transport, etc., and also develop a schedule/itinerary to follow for their trip. This project makes use of ReactJS, Sass, and Mapbox API to come up with a beautiful frontend for the proposed application. As this is a frontend only the data shown is dummy and the website is static apart from the maps which fetch real-time data from the Mapbox API. In this article, we will dive deeper into the steps that led to the creation of this immensely fascinating project!

ReactApp-GoogleChrome2024-03-2107-30-16-ezgifcom-video-to-gif-converter

Preview of the final website

Prequisites

Approach to Developing a Budget Travel Planner Usin React

  • Various frontend pages and interactions will be created using react, sass, and routing will be done using react-router-dom.
  • Login/Register Page: Users who want to plan trips will have to login/register
  • Landing Page: The landing page will have search features, map containing all the destinations that the user can pick from and popular destinations carousel.
  • Dashboard: This is a page where user can see all the trips they’ve planned and upcoming trips
  • Destination: This is a page which shows details about a particular destination like images, its location on map, about, attractions and activities etc. If interested users can start planning a trip to this destination.
  • Planner: This page helps the user in planning the budget of the trip, booking flights and hotels, creating a schedule and viewing their created schedule on a calender.
  • Maps: We will use Mapbox’s geocoding API to be able to search places. Based on our query it returns top 5 results that match our search and when we click on it the map shows the location of the place. On clicking the marker we can navigate to the place’s destination page.

Steps to Create React App and Installing Module

Step 1: Create a react application using the following command:

npx create-react-app JourneyDash

Step 2: Navigate to the root directory of your project using the following command:

cd JourneyDash

Step 3: Install npm packages such that the dependencies should be looking like this:

npm install @fortawesome/free-regular-svg-icons @fortawesome/free-solid-svg-icons @fortawesome/react-fontawesome 
dotenv mapbox-gl react-map-gl react-router-dom sass

Project Structure

Screenshot-2024-03-17-120730

Project Structure

package.json

"dependencies": {
"@fortawesome/free-regular-svg-icons": "^6.5.1",
"@fortawesome/free-solid-svg-icons": "^6.5.1",
"@fortawesome/react-fontawesome": "^0.2.0",
"@testing-library/jest-dom": "^5.17.0",
"@testing-library/react": "^13.4.0",
"@testing-library/user-event": "^13.5.0",
"dotenv": "^16.4.5",
"mapbox-gl": "^3.2.0",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-map-gl": "^7.1.7",
"react-router-dom": "^6.22.3",
"react-scripts": "5.0.1",
"sass": "^1.72.0",
"web-vitals": "^2.1.4"
},

Step 4: Create an account on mapbox and create an access token there. Now create .env file and add your mapbox token like so:

REACT_APP_MAPBOX_TOKEN = <your-access-token>

Step 5: Add the .env file to .gitignore file.

Example: Below is an example of Developing a Budget Travel Planner using React and Interactive maps (Mapbox).

HTML
<!-- client/public/index.html -->

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <link rel="preconnect" href="https://fonts.googleapis.com">
    <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
    <link href="https://fonts.googleapis.com/css2?family=Quicksand:wght@300..700&family=Truculenta:opsz,wght@12..72,100..900&display=swap" rel="stylesheet">
    <title>React App</title>
  </head>
  <body>
    <div id="root"></div>
  </body>
</html>
CSS
/* client/src/index.css */

body {
    margin: 0;
    padding: 0;
    font-family: "Quicksand", sans-serif;
    font-optical-sizing: auto;
}

:root {
    --bright-blue: #5356FF;
    --light-blue: #DFF5FF;
    --sky-blue: #67C6E3;
    --blue: #378CE7;
    --dark-blue: #11009E;
    --light-pink: #FAE7F3;
    --pink: #E6B9DE;
    --turquoise: #52D3D8;
    --black: #200E3A;
    --cursive: "Truculenta", sans-serif;
    ;
}
CSS
/* client/src/styles/home.scss */

.home {

    .home-wrapper {
        display: flex;
        flex-direction: column;
        margin-top: 100px;
        align-items: center;
        justify-content: center;
        width: 100%;
        flex-wrap: wrap;

        .head {
            margin: 30px 0 50px 0;
            letter-spacing: 1px;
            font-family: var(--cursive);
        }

        .map {
            position: relative;
            margin-bottom: 30px;

            .search {
                position: absolute;
                top: 10%;
                z-index: 100;
                left: 50%;
                transform: translate(-50%, -50%);

                .search-bar {
                    width: 500px;
                    display: flex;
                    justify-content: center;

                    input {
                        padding: 10px;
                        font-size: 1.2rem;
                        font-family: "Quicksand", sans-serif;
                        width: 300px;
                        border-radius: 5px;
                    }
                }

                .searches {
                    background-color: white;
                    display: flex;
                    flex-direction: column;
                    gap: 5px;
                    width: 500px;
                    position: fixed;

                    p {
                        padding: 5px;
                        cursor: pointer;

                        &:hover {
                            background-color: var(--light-blue);
                        }
                    }
                }
            }
        }

        .recommendations {
            display: flex;
            width: 90%;
            justify-content: center;
            align-items: center;
            margin-bottom: 50px;
            gap: 10px;
        }
    }

}
CSS
/* client/src/styles/login.scss */

.loginPage {
    display: grid;
    place-items: center;
    background-color: var(--light-pink);
    height: 100vh;
    width: 100vw;

    .title {
        color: var(--black);
        margin-bottom: 50px;
        text-align: center;
        font-size: 2rem;
        letter-spacing: 1px;
        font-family: var(--cursive);
    }

    .icon {
        block-size: 1em;
        display: inline-block;
        fill: var(--bright-blue);
        inline-size: 1em;
        vertical-align: middle;
    }

    .grid {
        inline-size: 90%;
        margin-inline: auto;
        max-inline-size: 20rem;


        .form {
            display: grid;
            gap: var(--formGap);
        }

        .form input[type="password"],
        .form input[type="text"],
        .form input[type="submit"] {
            inline-size: 100%;
        }

        .login {
            color: var(--blue);
        }

        .login label,
        .login input[type="text"],
        .login input[type="password"],
        .login input[type="submit"] {
            border-radius: 0.25rem;
            padding: 1rem;
        }

        .login label {
            background-color: var(--light-blue);
            border-bottom-right-radius: 0;
            border-top-right-radius: 0;
            padding-inline: 1.25rem;
        }

        .login input[type="password"],
        .login input[type="text"] {
            background-color: white;
            border-bottom-left-radius: 0;
            border-top-left-radius: 0;
        }

        .login input[type="password"]:focus,
        .login input[type="password"]:hover,
        .login input[type="text"]:focus,
        .login input[type="text"]:hover {
            background-color: white;
        }

        .login input[type="submit"] {
            background-color: var(--blue);
            color: white;
            font-weight: 700;
            text-transform: uppercase;
        }

        .login input[type="submit"]:focus,
        .login input[type="submit"]:hover {
            background-color: var(--dark-blue);
        }

        .form__field {

            display: flex;


            .form__input {
                flex: 1;
            }

            .hidden {
                border: 0;
                clip: rect(0 0 0 0);
                height: 1px;
                margin: -1px;
                overflow: hidden;
                padding: 0;
                position: absolute;
                width: 1px;
            }

            input {
                background-image: none;
                border: 0;
                font: inherit;
                margin: 0;
                outline: 0;
                padding: 0;
                transition: background-color 0.3s;

                &::placeholder {
                    color: var(--dark-blue);
                }
            }

            input[type="submit"] {
                cursor: pointer;
            }
        }

    }

    .icons {
        display: none;
    }

    a {
        color: var(--blue);
        outline: 0;
        text-decoration: none;
    }

    a:focus,
    a:hover {
        color: var(--dark-blue);
    }

    p {
        margin-block: 1.5rem;
    }

    .text--center {
        text-align: center;
    }
}

/* modules/form.css */

:root {
    --formGap: 0.875rem;
}
CSS
/* client/src/styles/register.scss */

.registerPage {
    display: grid;
    place-items: center;
    background-color: var(--light-pink);
    height: 100vh;
    width: 100vw;

    .title {
        color: var(--black);
        margin-bottom: 50px;
        text-align: center;
        font-size: 2rem;
        letter-spacing: 1px;
        font-family: var(--cursive);
    }

    .icon {
        block-size: 1em;
        display: inline-block;
        fill: var(--bright-blue);
        inline-size: 1em;
        vertical-align: middle;
    }

    .grid {
        inline-size: 90%;
        margin-inline: auto;
        max-inline-size: 20rem;


        .form {
            display: grid;
            gap: var(--formGap);
        }

        .form input[type="password"],
        .form input[type="text"],
        .form input[type="submit"] {
            inline-size: 100%;
        }

        .login {
            color: var(--blue);
        }

        .login label,
        .login input[type="text"],
        .login input[type="password"],
        .login input[type="submit"] {
            border-radius: 0.25rem;
            padding: 1rem;
        }

        .login label {
            background-color: var(--light-blue);
            border-bottom-right-radius: 0;
            border-top-right-radius: 0;
            padding-inline: 1.25rem;
        }

        .login input[type="password"],
        .login input[type="text"] {
            background-color: white;
            border-bottom-left-radius: 0;
            border-top-left-radius: 0;
        }

        .login input[type="password"]:focus,
        .login input[type="password"]:hover,
        .login input[type="text"]:focus,
        .login input[type="text"]:hover {
            background-color: white;
        }

        .login input[type="submit"] {
            background-color: var(--blue);
            color: white;
            font-weight: 700;
            text-transform: uppercase;
        }

        .login input[type="submit"]:focus,
        .login input[type="submit"]:hover {
            background-color: var(--dark-blue);
        }

        .form__field {

            display: flex;


            .form__input {
                flex: 1;
            }

            .hidden {
                border: 0;
                clip: rect(0 0 0 0);
                height: 1px;
                margin: -1px;
                overflow: hidden;
                padding: 0;
                position: absolute;
                width: 1px;
            }

            input {
                background-image: none;
                border: 0;
                font: inherit;
                margin: 0;
                outline: 0;
                padding: 0;
                transition: background-color 0.3s;

                &::placeholder {
                    color: var(--dark-blue);
                }
            }

            input[type="submit"] {
                cursor: pointer;
            }
        }

    }

    .icons {
        display: none;
    }

    a {
        color: var(--blue);
        outline: 0;
        text-decoration: none;
    }

    a:focus,
    a:hover {
        color: var(--dark-blue);
    }

    p {
        margin-block: 1.5rem;
    }

    .text--center {
        text-align: center;
    }
}

/* modules/form.css */

:root {
    --formGap: 0.875rem;
}
CSS
/* client/src/styles/dashboard.scss */

.dashboard {
    background-color: var(--light-blue);

    .dashboard-wrapper {
        margin-top: 60px;
        display: flex;
        width: 100%;
        flex-direction: column;
        align-items: center;
        justify-content: center;
        gap: 50px;

        h1 {
            margin-bottom: 20px;
            font-family: var(--cursive);
        }

        .upperContent {
            display: flex;
            width: 90%;
            align-items: center;
            justify-content: center;
            gap: 50px;


            .leftContainer {
                height: 400px;
                width: 45%;
                display: flex;
                background-color: white;
                margin-top: 40px;
                border-radius: 10px;
                flex-direction: column;
                padding: 30px;
                gap: 10px;
                align-items: center;
                justify-content: center;

                .trip-container {
                    display: flex;
                    background-color: var(--light-pink);
                    padding: 10px 20px;
                    width: 400px;
                    gap: 30px;

                    .details {
                        display: flex;
                        flex-direction: column;
                        justify-content: space-around;

                        .icon {
                            color: var(--blue);
                        }
                    }

                    img {
                        height: 70px;
                        width: 100px;
                        object-fit: cover;
                    }
                }
            }

            .rightContainer {
                height: 400px;
                font-size: 1.2rem;
                padding: 30px;
                margin-top: 40px;
                background-color: white;
                border-radius: 10px;
                width: 45%;
                display: flex;
                flex-direction: column;
                gap: 10px;
                align-items: center;
                justify-content: center;

                #first {
                    color: var(--blue);
                    margin-right: 10px;

                }

                .icon {

                    margin-right: 10px;
                    color: var(--sky-blue);
                }

                .wish-button {
                    margin-top: 10px;
                    padding: 10px 20px;
                    background-color: var(--blue);
                    color: white;
                    cursor: pointer;
                    outline: none;
                    border: none;
                    transition: all ease-in-out 0.3s;

                    &:hover {
                        transform: translateY(-2px);
                    }
                }
            }
        }

        .lowerContent {
            width: 86.5%;
            display: flex;
            border-radius: 10px;
            background-color: white;
            padding: 50px;
            justify-content: center;
            gap: 50px;
            margin-bottom: 50px;

            .side {
                display: flex;
                flex-direction: column;
                gap: 20px;

                img {
                    width: 200px;
                    height: 100px;
                    object-fit: cover;
                    border-radius: 10px;
                }

                p {
                    font-size: 1.1rem;

                    span {
                        color: var(--black);
                        font-weight: 700;
                        background-color: var(--light-blue);
                        padding: 5px 10px;
                    }
                }

                section {
                    text-align: center;
                    padding: 20px;
                    background-color: var(--light-pink);
                    font-size: 1.1rem;
                    width: 200px;
                    margin-top: 20px;

                    span {
                        margin: 0 14px;
                    }
                }
            }
        }
    }
}
CSS
/* client/src/styles/destination.scss */

.destination {

    .destination-container {

        display: flex;
        flex-direction: column;
        align-items: center;
        justify-content: center;

        .header {

            margin-top: 100px;

            display: flex;
            gap: 100px;
            padding: 0 80px;
            align-items: center;
            justify-content: center;

            img {
                height: 400px;
                width: 500px;
                clip-path: polygon(9% 0, 100% 0%, 91% 100%, 0% 100%);
                object-fit: cover;
            }

            .details {
                display: flex;
                flex-direction: column;
                gap: 20px;
                width: 60%;

                h1 {
                    font-family: var(--cursive);
                    font-size: 3rem;
                }

                p {
                    span {
                        color: var(--black);
                        font-weight: 700;
                        background-color: var(--light-blue);
                        padding: 5px 10px;
                        margin: 0 5px;
                    }
                }

                section {
                    text-align: center;
                    padding: 20px;
                    background-color: var(--light-pink);
                    font-size: 1.1rem;
                    width: 200px;
                    margin-top: 20px;

                    span {
                        margin: 0 14px;
                    }
                }
            }
        }

        .location-map {
            margin: 50px auto;
        }

        .plan-trip-container {
            h1 {
                text-align: center;
                font-family: var(--cursive);
            }

            width: 800px;
            padding: 20px;

            .plan-input {
                padding: 15px 15px 15px 50px;
                margin: 20px;

                input {
                    border: none;
                    font-size: 1.2rem;
                    padding: 5px;
                    margin-left: 10px;
                    font-family: "Quicksand", sans-serif;
                    ;
                }
            }

            /* Alternate background colors */
            .plan-input:nth-child(odd) {
                background-color: var(--light-blue);
            }

            .plan-input:nth-child(even) {
                background-color: var(--light-pink);
            }

            h3,
            h2 {
                text-align: center;
            }
        }
    }
}
CSS
/* client/src/styles/navbar.scss */

* {
    margin: 0;
    padding: 0;
    text-decoration: none;
}

.navContainer {
    overflow: hidden;
    position: fixed;
    top: 0;
    left: 0;
    right: 0;
    background-color: var(--blue);
    box-shadow: 0 5px 10px rgba(0, 0, 0, 0.1);
    padding: 0px 7%;
    display: flex;
    align-items: center;
    justify-content: space-between;
    z-index: 100;
}


.navLogo {
    color: white;
    font-family: var(--cursive);
    letter-spacing: 2px;
    font-weight: 900;
    font-size: 1.5rem;
    font-style: italic;
}

.navbar ul {
    list-style: none;
}

.navbar ul li {
    position: relative;
    float: left;
}

.profilePicture {
    height: 40px;
    width: 40px;
}

.profilePicture img {
    margin-top: 10px;
    height: 40px;
    width: 40px;
    border-radius: 50%;
    object-fit: cover;
}

#usernamename {
    display: none;
}

.navbar ul li p {
    font-size: 1rem;
    padding: 20px;
    color: white;
    display: block;
    transition: all 1s;
}

.navbar ul li p:hover {
    transform: translateY(-1px);
    border-bottom: solid 2px white;
}

#menu-bar {
    display: none;
}

.navContainer label {
    font-size: 1.5rem;
    color: white;
    cursor: pointer;
    display: none;
}

@media (max-width:800px) {
    .navContainer {
        height: 70px;
    }

    .navContainer label {
        display: initial;
    }

    .navContainer .navbar {
        position: fixed;
        top: 70px;
        left: -100%;
        text-align: center;
        background: white;
        border-top: 1px solid rgba(0, 0, 0, 0.1);
        display: block;
        transition: all 0.3s ease;
        width: 100%;
    }

    .profilePicture {
        display: none;
    }

    #usernamename {
        font-weight: bolder;
        display: block;
    }

    .navbar ul li p {
        color: black;
    }

    .navbar ul li p:hover {
        transform: translateY(-1px);

        border-bottom: none;
    }

    .navbar ul li {
        width: 100%;
    }

    #menu-bar:checked~.navbar {
        left: 0;
    }
}
CSS
/* client/src/styles/navbar.scss */

* {
    margin: 0;
    padding: 0;
    text-decoration: none;
}

.navContainer {
    overflow: hidden;
    position: fixed;
    top: 0;
    left: 0;
    right: 0;
    background-color: var(--blue);
    box-shadow: 0 5px 10px rgba(0, 0, 0, 0.1);
    padding: 0px 7%;
    display: flex;
    align-items: center;
    justify-content: space-between;
    z-index: 100;
}


.navLogo {
    color: white;
    font-family: var(--cursive);
    letter-spacing: 2px;
    font-weight: 900;
    font-size: 1.5rem;
    font-style: italic;
}

.navbar ul {
    list-style: none;
}

.navbar ul li {
    position: relative;
    float: left;
}

.profilePicture {
    height: 40px;
    width: 40px;
}

.profilePicture img {
    margin-top: 10px;
    height: 40px;
    width: 40px;
    border-radius: 50%;
    object-fit: cover;
}

#usernamename {
    display: none;
}

.navbar ul li p {
    font-size: 1rem;
    padding: 20px;
    color: white;
    display: block;
    transition: all 1s;
}

.navbar ul li p:hover {
    transform: translateY(-1px);
    border-bottom: solid 2px white;
}

#menu-bar {
    display: none;
}

.navContainer label {
    font-size: 1.5rem;
    color: white;
    cursor: pointer;
    display: none;
}

@media (max-width:800px) {
    .navContainer {
        height: 70px;
    }

    .navContainer label {
        display: initial;
    }

    .navContainer .navbar {
        position: fixed;
        top: 70px;
        left: -100%;
        text-align: center;
        background: white;
        border-top: 1px solid rgba(0, 0, 0, 0.1);
        display: block;
        transition: all 0.3s ease;
        width: 100%;
    }

    .profilePicture {
        display: none;
    }

    #usernamename {
        font-weight: bolder;
        display: block;
    }

    .navbar ul li p {
        color: black;
    }

    .navbar ul li p:hover {
        transform: translateY(-1px);

        border-bottom: none;
    }

    .navbar ul li {
        width: 100%;
    }

    #menu-bar:checked~.navbar {
        left: 0;
    }
}
JavaScript
// client/src/index.js

import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
import App from './App';

const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
    <React.StrictMode>
        <App />
    </React.StrictMode>
);
JavaScript
// client/src/App.js

import { BrowserRouter, Routes, Route } from "react-router-dom"
import Home from "./pages/Home";
import Login from "./pages/Login";
import Register from "./pages/Register";
import Dashboard from "./pages/Dashboard";
import Destination from "./pages/Destination";

function App() {
    return (
        <BrowserRouter>
            <Routes>
                <Route path="/" element={<Home />} />
                <Route path="/login" element={<Login />} />
                <Route path="/register" element={<Register />} />
                <Route path="/dashboard" element={<Dashboard />} />
                <Route path="/destination/:id" element={<Destination />} />
            </Routes>
        </BrowserRouter>
    );
}

export default App;
JavaScript
// client/src/pages/Home.jsx

import React, {
    useState,
    useEffect
} from 'react'
import Navbar from '../components/Navbar'
import RecCard from "../components/RecCard"
import Map, {
    Marker
} from "react-map-gl"
import 'mapbox-gl/dist/mapbox-gl.css';
import "../styles/home.scss"
import {
    Link
} from 'react-router-dom';

const Home = () => {

    const [places, setPlaces] = useState([]);
    const [value, setValue] = useState("");
    const [viewState, setViewState] = React.useState({
        latitude: 37.8,
        longitude: -122.4,
        zoom: 12
    });


    useEffect(() => {
        getPlaces();
    }, [value])

    const getPlaces = async () => {
        const promise = await fetch(`https://api.mapbox.com/geocoding/v5/mapbox.places/${value}.json?access_token=${process.env.REACT_APP_MAPBOX_TOKEN}`)
        const data = await promise.json();
        setPlaces(data.features);
        console.log(data)
    }

    const handleClick = (query) => {
        setValue(query.place_name)
        const { center } = query;
        setViewState((prevState) => ({
            ...prevState,
            latitude: center[1],
            longitude: center[0],
        }));
        setPlaces([])
    }

    const mockData = [
        {
            name: "New York",
            image: "https://media.geeksforgeeks.org/wp-content/uploads/20240321072009/landmark-statue-liberty-new-york-city-famous-landscape-buildings-statue-liberty-uas-tourist-attraction-design-postcard-travel-poster-vector-illustration_1150-56573-compressed.jpg"
        },
        {
            name: "Venice",
            image: "https://media.geeksforgeeks.org/wp-content/uploads/20240321072310/travel-around-world-colorful-poster_52683-28357.jpg"
        },
        {
            name: "Gangtok",
            image: "https://media.geeksforgeeks.org/wp-content/uploads/20240321072631/flat-ski-station_23-2148010938.jpg"
        },
        {
            name: "Cairo",
            image: "https://media.geeksforgeeks.org/wp-content/uploads/20240321072500/egyptian-night-desert-pyramids-sphinx-anubis_107791-1591.jpg"
        },
    ]

    return (
        <div className='home'>
            <Navbar />
            <div className="home-wrapper">
                <div className="map">
                    <div className="search">
                        <div className="search-bar">
                            <input placeholder='Search places' type="text"
                                name='place' id='place' value={value}
                                onChange={(e) => setValue(e.target.value)} />
                        </div>
                        <div className="searches">
                            {
                                places?.map((items, index) => {
                                    return (
                                        <div key={index} onClick={() => handleClick(items)}>
                                            <p>{items.place_name}</p>
                                        </div>
                                    )
                                })
                            }
                        </div>
                    </div>
                    <Link to={`/destination/${value}`}>
                        <Map
                            {...viewState}
                            onMove={evt => setViewState(evt.viewState)}
                            style={{ width: 800, height: 400 }}
                            mapStyle="mapbox://styles/mapbox/streets-v9"
                            mapboxAccessToken={process.env.REACT_APP_MAPBOX_TOKEN}
                        >
                            <Marker className="marker"
                                longitude={viewState.longitude}
                                latitude={viewState.latitude} color="red" />
                        </Map>
                    </Link>
                </div>
                <h1 className='head'>Recommendations for you</h1>
                <div className="recommendations">
                    {mockData.map((d, index) => (
                        <RecCard key={index} image={d.image} name={d.name} />
                    ))}
                </div>
            </div>
        </div>
    )
}

export default Home
JavaScript
// client/src/pages/Login.jsx

import React from 'react'
import Navbar from "../components/Navbar"
import "../styles/login.scss"

const Login = () => {
    return (
        <div className='loginPage'>
            <Navbar />
            <div class="grid">

                <h2 className='title'>Welcome Back!</h2>

                <form action="https://httpbin.org/post" method="POST" class="form login">

                    <div class="form__field">
                        <label for="login__username"><svg class="icon">
                            <use href="#icon-user"></use>
                        </svg><span class="hidden">Username</span></label>
                        <input autocomplete="username" id="login__username" type="text"
                            name="username" class="form__input" placeholder="Username" required />
                    </div>

                    <div class="form__field">
                        <label for="login__password"><svg class="icon">
                            <use href="#icon-lock"></use>
                        </svg><span class="hidden">Password</span></label>
                        <input id="login__password" type="password" name="password"
                            class="form__input" placeholder="Password" required />
                    </div>

                    <div class="form__field">
                        <input type="submit" value="Sign In" />
                    </div>

                </form>

                <p class="text--center">Not a member?
                    <a href="/register">Sign up now</a>
                    <svg class="icon">
                        <use href="#icon-arrow-right"></use>
                    </svg></p>


            </div>
            <svg xmlns="http://www.w3.org/2000/svg" class="icons">
                <symbol id="icon-arrow-right" viewBox="0 0 1792 1792">
                    <path
                        d="M1600 960q0 54-37 91l-651 651q-39 37-91 37-51 0-90-37l-75-75q-38-38-38-91t38-91l293-293H245q-52 0-84.5-37.5T128 1024V896q0-53 32.5-90.5T245 768h704L656 474q-38-36-38-90t38-90l75-75q38-38 90-38 53 0 91 38l651 651q37 35 37 90z" />
                </symbol>
                <symbol id="icon-lock" viewBox="0 0 1792 1792">
                    <path
                        d="M640 768h512V576q0-106-75-181t-181-75-181 75-75 181v192zm832 96v576q0 40-28 68t-68 28H416q-40 0-68-28t-28-68V864q0-40 28-68t68-28h32V576q0-184 132-316t316-132 316 132 132 316v192h32q40 0 68 28t28 68z" />
                </symbol>
                <symbol id="icon-user" viewBox="0 0 1792 1792">
                    <path
                        d="M1600 1405q0 120-73 189.5t-194 69.5H459q-121 0-194-69.5T192 1405q0-53 3.5-103.5t14-109T236 1084t43-97.5 62-81 85.5-53.5T538 832q9 0 42 21.5t74.5 48 108 48T896 971t133.5-21.5 108-48 74.5-48 42-21.5q61 0 111.5 20t85.5 53.5 62 81 43 97.5 26.5 108.5 14 109 3.5 103.5zm-320-893q0 159-112.5 271.5T896 896 624.5 783.5 512 512t112.5-271.5T896 128t271.5 112.5T1280 512z" />
                </symbol>
            </svg>
        </div>
    )
}

export default Login
JavaScript
// client/src/pages/Register.jsx

import React from 'react'
import { faPhone } from '@fortawesome/free-solid-svg-icons'
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import Navbar from "../components/Navbar"
import "../styles/register.scss"

const Register = () => {
    return (
        <div className='registerPage'>
            <Navbar />
            <div class="grid">

                <h2 className='title'>Join our Community!</h2>

                <form action="https://httpbin.org/post" method="POST"
                    class="form login">

                    <div class="form__field">
                        <label for="login__username"><svg class="icon">
                            <use href="#icon-user"></use>
                        </svg><span class="hidden">Name</span></label>
                        <input autocomplete="username" id="login__username"
                            type="text" name="username" class="form__input"
                            placeholder="Name" required />
                    </div>

                    <div class="form__field">
                        <label for="login__username">
                            <svg class="icon">
                                <use href="#icon-user"></use>
                            </svg>
                            <span class="hidden">Username</span>
                        </label>
                        <input autocomplete="username" id="login__username"
                            type="text" name="username" class="form__input"
                            placeholder="Username" required />
                    </div>

                    <div class="form__field">
                        <label for="login__username">
                            <FontAwesomeIcon icon={faPhone} className="icon" />
                            <span class="hidden">Phone Number</span>
                        </label>
                        <input autocomplete="username" id="login__username"
                            type="text" name="username" class="form__input"
                            placeholder="Phone Number" required />
                    </div>

                    <div class="form__field">
                        <label for="login__password"><svg class="icon">
                            <use href="#icon-lock"></use>
                        </svg><span class="hidden">Password</span></label>
                        <input id="login__password" type="password" name="password"
                            class="form__input" placeholder="Password" required />
                    </div>

                    <div class="form__field">
                        <input type="submit" value="Sign In" />
                    </div>

                </form>

                <p class="text--center">Already a member?
                    <a href="/login">Sign In</a>
                    <svg class="icon">
                        <use href="#icon-arrow-right"></use>
                    </svg></p>


            </div>
            <svg xmlns="http://www.w3.org/2000/svg" class="icons">
                <symbol id="icon-arrow-right" viewBox="0 0 1792 1792">
                    <path
                        d="M1600 960q0 54-37 91l-651 651q-39 37-91 37-51 0-90-37l-75-75q-38-38-38-91t38-91l293-293H245q-52 0-84.5-37.5T128 1024V896q0-53 32.5-90.5T245 768h704L656 474q-38-36-38-90t38-90l75-75q38-38 90-38 53 0 91 38l651 651q37 35 37 90z" />
                </symbol>
                <symbol id="icon-lock" viewBox="0 0 1792 1792">
                    <path
                        d="M640 768h512V576q0-106-75-181t-181-75-181 75-75 181v192zm832 96v576q0 40-28 68t-68 28H416q-40 0-68-28t-28-68V864q0-40 28-68t68-28h32V576q0-184 132-316t316-132 316 132 132 316v192h32q40 0 68 28t28 68z" />
                </symbol>
                <symbol id="icon-user" viewBox="0 0 1792 1792">
                    <path
                        d="M1600 1405q0 120-73 189.5t-194 69.5H459q-121 0-194-69.5T192 1405q0-53 3.5-103.5t14-109T236 1084t43-97.5 62-81 85.5-53.5T538 832q9 0 42 21.5t74.5 48 108 48T896 971t133.5-21.5 108-48 74.5-48 42-21.5q61 0 111.5 20t85.5 53.5 62 81 43 97.5 26.5 108.5 14 109 3.5 103.5zm-320-893q0 159-112.5 271.5T896 896 624.5 783.5 512 512t112.5-271.5T896 128t271.5 112.5T1280 512z" />
                </symbol>
            </svg>
        </div>
    )
}

export default Register
JavaScript
// client/src/pages/Dashboard.jsx

import React from 'react'
import "../styles/dashboard.scss"
import {
    faCheckCircle,
    faCheck,
    faTemperature0,
    faSun
} from '@fortawesome/free-solid-svg-icons'
import {
    FontAwesomeIcon
} from "@fortawesome/react-fontawesome";
import Navbar from '../components/Navbar'

const Dashboard = () => {
    return (
        <div className='dashboard'>
            <Navbar />
            <div className="dashboard-wrapper">

                <div className="upperContent">
                    <div className="leftContainer">
                        <h1>Planned Trips</h1>
                        <div className="trip-container">
                            <img src="https://media.geeksforgeeks.org/wp-content/uploads/20240321072009/landmark-statue-liberty-new-york-city-famous-landscape-buildings-statue-liberty-uas-tourist-attraction-design-postcard-travel-poster-vector-illustration_1150-56573-compressed.jpg" alt="" />
                            <div className="details">
                                <h3>New York</h3>
                                <p>23rd March - 25th March</p>
                            </div>
                        </div>
                        <div className="trip-container">
                            <img src="https://media.geeksforgeeks.org/wp-content/uploads/20240321072310/travel-around-world-colorful-poster_52683-28357.jpg" alt="" />
                            <div className="details">
                                <h3>Venice</h3>
                                <p>15th April - 20th April</p>
                            </div>
                        </div>
                        <div className="trip-container">
                            <img src="https://media.geeksforgeeks.org/wp-content/uploads/20240321072631/flat-ski-station_23-2148010938.jpg" alt="" />
                            <div className="details">
                                <h3>Gangtok</h3>
                                <p>7th June - 10th June</p>
                            </div>
                        </div>
                    </div>
                    <div className="rightContainer">
                        <h1>Wishlist</h1>
                        <div className="wish-container">
                            <FontAwesomeIcon icon={faCheckCircle} id='first' />
                            To drink a coffee in Paris
                        </div>
                        <div className="wish-container">
                            <FontAwesomeIcon icon={faCheck} className="icon" />
                            To climb Mount Kailash
                        </div>
                        <div className="wish-container">
                            <FontAwesomeIcon icon={faCheck} className="icon" />
                            To stand under the Northern Lights
                        </div>
                        <div className="wish-container">
                            <FontAwesomeIcon icon={faCheck} className="icon" />
                            To pick up glowing water in my hands
                        </div>
                        <div className="wish-container">
                            <FontAwesomeIcon icon={faCheck} className="icon" />
                            To light a flying lantern in Thailand
                        </div>

                        <button className="wish-button">Add New Wish</button>
                    </div>
                </div>
                <div className="lowerContent">
                    <div className="side">
                        <h1>Upcoming Tour</h1>
                        <p>Cairo, Egypt, 17th March</p>
                        <img src="https://media.geeksforgeeks.org/wp-content/uploads/20240321072500/egyptian-night-desert-pyramids-sphinx-anubis_107791-1591.jpg" alt="" />
                    </div>
                    <div className="side">
                        <h3>About</h3>
                        <p>Cairo, Egypt's capital, is a sprawling metropolis
                            along the Nile River. Known for its ancient landmarks
                            like the Pyramids of Giza and the Sphinx, it's a cultural
                            hub with museums, mosques, and vibrant markets.
                            It blends historic charm with modern energy,
                            offering visitors a glimpse into Egypt's rich heritage.
                        </p>
                        <h3>Itenerary</h3>
                        <p><span>Pyramids of Giza
                        </span> - <span>
                                Egyptian Museum
                            </span> - <span>
                                Salah El-Din Citadel
                            </span> - <span>
                                Cairo Tower
                            </span> - <span>
                                Khan El Khalili
                            </span></p>
                        <section>
                            <FontAwesomeIcon icon={faTemperature0} className="icon" />
                            <span>30-35</span>
                            <FontAwesomeIcon icon={faSun} className="icon" />
                            <span>All days</span>
                        </section>
                    </div>
                </div>
            </div>
        </div>
    )
}

export default Dashboard
JavaScript
// client/src/pages/Destination.jsx

import React, {
    useState
} from 'react'
import {
    faMoneyBill1Wave,
    faWalking
} from '@fortawesome/free-solid-svg-icons'
import {
    FontAwesomeIcon
} from "@fortawesome/react-fontawesome";
import Navbar from '../components/Navbar';
import "../styles/destination.scss"
import Map, {
    Marker
} from 'react-map-gl';
import 'mapbox-gl/dist/mapbox-gl.css';

const Destination = () => {

    const [viewState, setViewState] = React.useState({
        latitude: 35.6764,
        longitude: 139.7300,
        zoom: 10
    });

    const [accommodationPrice, setAccommodationPrice] = useState(0);
    const [flightDeparturePrice, setFlightDeparturePrice] = useState(0);
    const [flightReturnPrice, setFlightReturnPrice] = useState(0);
    const [transportPrice, setTransportPrice] = useState(0);
    const [mealsPrice, setMealsPrice] = useState(0);
    const [activitiesPrice, setActivitiesPrice] = useState(0);

    const calculateTotalBudget = () => {
        return (
            parseFloat(accommodationPrice) +
            parseFloat(flightDeparturePrice) +
            parseFloat(flightReturnPrice) +
            parseFloat(transportPrice) +
            parseFloat(mealsPrice) +
            parseFloat(activitiesPrice)
        );
    };

    return (
        <div className='destination'>
            <Navbar />
            <div className="destination-container">
                <div className="header">
                    <img src="https://media.geeksforgeeks.org/wp-content/uploads/20240321072734/hand-drawn-japanese-castle-illustration_52683-46247-compressed.jpg" alt="" />
                    <div className="details">
                        <h1>Tokyo, Japan</h1>
                        <p>Tokyo, the capital of Japan, is a vibrant metropolis
                            where traditional culture meets futuristic innovation.
                            It boasts iconic landmarks like the Tokyo Tower
                            and Skytree, bustling districts like Shibuya and
                            Shinjuku, serene shrines and temples, and a diverse
                            culinary scene. Tokyo is a dynamic blend of history,
                            technology, and culture.
                        </p>
                        <p>
                            <span>Vibrant</span>
                            <span>Modern</span>
                            <span>Bustling</span>
                            <span>Electric</span>
                            <span>Cultural</span>
                        </p>
                        <section>
                            <FontAwesomeIcon icon={faMoneyBill1Wave}
                                className="icon" />
                            <span>$600</span>
                            <FontAwesomeIcon icon={faWalking}
                                className="icon" />
                            <span>2-3 days</span>
                        </section>
                    </div>
                </div>
                <div className="location-map">
                    <Map
                        {...viewState}
                        onMove={evt => setViewState(evt.viewState)}
                        style={{ width: 1500, height: 300 }}
                        mapStyle="mapbox://styles/mapbox/streets-v9"
                        mapboxAccessToken={process.env.REACT_APP_MAPBOX_TOKEN}
                    >
                        <Marker className="marker"
                            longitude={viewState.longitude}
                            latitude={viewState.latitude} color="red" />
                    </Map>
                </div>


                <div className="plan-trip-container">
                    <h1>Start Planning Trip to Tokyo</h1>
                    <div className="form-container">
                        <div className="plan-input">
                            <label>Start Date:</label>
                            <input type="date" />
                        </div>
                        <div className="plan-input">
                            <label>End Date:</label>
                            <input type="date" />
                        </div>
                        <div className="plan-input">
                            <label>No of People:</label>
                            <input type="number" />
                        </div>

                        <div>
                            <h3>Accommodation</h3>

                            <div className="plan-input">
                                <label>Accommodation Name:</label>
                                <input type="text" />
                            </div>
                            <div className="plan-input">
                                <label>Price: $</label>
                                <input type="number" value={accommodationPrice}
                                    onChange={(e) => setAccommodationPrice(e.target.value)} />
                            </div>
                        </div>
                        <div>
                            <h3>Flights</h3>

                            <div className="plan-input">
                                <label htmlFor>Enter Flight Name: </label>
                                <input type="text" />
                            </div>
                            <div className="plan-input">
                                <label>Flight Departure Price: $</label>
                                <input type="number" value={flightDeparturePrice}
                                    onChange={(e) => setFlightDeparturePrice(e.target.value)} />
                            </div>
                            <div className="plan-input">
                                <label htmlFor>Enter Flight Name: </label>
                                <input type="text" />
                            </div>
                            <div className="plan-input">
                                <label>Flight Return Price: $</label>
                                <input type="number" value={flightReturnPrice}
                                    onChange={(e) => setFlightReturnPrice(e.target.value)} />
                            </div>
                        </div>
                        <div>
                            <h3>Transportation</h3>

                            <div className="plan-input">
                                <label>Transport Type</label>
                                <input type="text" />
                            </div>
                            <div className="plan-input">
                                <label>Transport Cost: $</label>
                                <input type="number" value={transportPrice}
                                    onChange={(e) => setTransportPrice(e.target.value)} />
                            </div>
                        </div>
                        <div>
                            <h3>Meals & Food</h3>

                            <div className="plan-input">
                                <label htmlFor>Enter Meals to Try: </label>
                                <input type="text" />
                            </div>
                            <div className="plan-input">
                                <label>Meals & Snacks Price: $</label>
                                <input type="number" value={mealsPrice}
                                    onChange={(e) => setMealsPrice(e.target.value)} />
                            </div>
                        </div>
                        <div>
                            <h3>Activities & Tourist Spots</h3>
                            <div className="plan-input">
                                <label htmlFor>Enter Activities to Try: </label>
                                <input type="text" />
                            </div>
                            <div className="plan-input">
                                <label htmlFor>Enter Tourist Spots to Explore: </label>
                                <input type="text" />
                            </div>
                            <div className="plan-input">
                                <label>Activities & Tourist Spots Price: $</label>
                                <input type="number" value={activitiesPrice}
                                    onChange={(e) => setActivitiesPrice(e.target.value)} />
                            </div>
                        </div>
                        <div>
                            <h2>Total Budget: {calculateTotalBudget()}</h2>
                        </div>
                    </div>
                </div>
            </div>
        </div>
    )
}

export default Destination
JavaScript
// client/src/components/Navbar.jsx

import '../styles/navbar.scss'

import {
    faBars
} from '@fortawesome/free-solid-svg-icons'
import {
    FontAwesomeIcon
} from "@fortawesome/react-fontawesome";
import {
    Link
} from "react-router-dom"



const Navbar = () => {

    return (
        <div className='navContainer'>
            <Link to="/">
                <p className='navLogo'>JourneyDash</p>
            </Link>

            <input type="checkbox" id='menu-bar' />
            <label htmlFor="menu-bar">
                <FontAwesomeIcon icon={faBars}
                    className="icon" />
            </label>
            <nav className='navbar'>
                <ul>
                    <Link to="/">
                        <li><p>Home</p></li>
                    </Link>
                    <Link to="/dashboard">
                        <li><p>Dashboard</p></li>
                    </Link>
                    <Link to="/login">
                        <li><p>Login</p></li>
                    </Link>
                    <Link to="/register">
                        <li><p>Register</p></li>
                    </Link>
                </ul>
            </nav>
        </div >
    )
}

export default Navbar
JavaScript
// client/src/components/RecCard.jsx

import React from 'react'
import "../styles/recCard.scss"
import {
    Link
} from 'react-router-dom'

const RecCard = ({ image, name }) => {
    return (
        <div className='recCardContainer'>
            <Link to={`/destination/${name}`}>
                <img src={image} alt="" />
                <div className="rec-name">
                    {name}
                </div>
            </Link>
        </div>
    )
}

export default RecCard

Start your application using the following command:

npm start

Output:

ReactApp-GoogleChrome2024-03-2107-30-34-ezgifcom-video-to-gif-converter

Final Preview of the Website



Like Article
Suggest improvement
Share your thoughts in the comments

Similar Reads