Open In App

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

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

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).

<!-- 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>
/* 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;
    ;
}
/* 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;
        }
    }

}
/* 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;
}
/* 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;
}
/* 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;
                    }
                }
            }
        }
    }
}
/* 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;
            }
        }
    }
}
/* 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;
    }
}
/* 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;
    }
}
// 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>
);
// 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;
// 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
// 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
// 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
// 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
// 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
// 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
// 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

Article Tags :