52 | )}
53 | >
54 | );
55 | }
--------------------------------------------------------------------------------
/src/App.js:
--------------------------------------------------------------------------------
1 | import {BrowserRouter as Router, Routes, Route} from "react-router-dom"
2 | import Home from "./pages/Home"
3 | import Profile from "./pages/Profile"
4 | import SignIn from "./pages/SignIn"
5 | import SignUp from "./pages/SignUp"
6 | import PrivateRoute from "./components/PrivateRoute"
7 | import ForgotPassword from "./pages/ForgotPassword"
8 | import Offers from "./pages/Offers"
9 | import Header from "./components/Header"
10 | import { ToastContainer } from "react-toastify"
11 | import 'react-toastify/dist/ReactToastify.css';
12 | import CreateListing from "./pages/CreateListing"
13 | import EditListing from "./pages/EditListing"
14 | import Listing from "./pages/Listing";
15 | import Category from "./pages/Category"
16 |
17 |
18 | function App() {
19 | return (
20 | <>
21 |
22 |
23 |
24 | }/>
25 | {/* Protected the profile page by putting it inside another router
26 | and with the path of profile */}
27 | }>
28 | }/>
29 |
30 | }/>
31 | }/>
32 | }/>
33 | }
34 | />
35 | }/>
36 | }/>
37 | {/* Protected the profile page by putting it inside another router
38 | and with the path of profile */}
39 | }>
40 | }/>
41 |
42 | {/* Protected the profile page by putting it inside another router
43 | and with the path of profile */}
44 | }>
45 | }/>
46 |
47 |
48 |
49 |
61 | >
62 | );
63 | }
64 |
65 | export default App;
66 |
--------------------------------------------------------------------------------
/src/components/Header.jsx:
--------------------------------------------------------------------------------
1 | import React, { useEffect, useState } from 'react';
2 | import { useLocation, useNavigate } from 'react-router-dom';
3 | import IMG from "../img/realtor-logo.svg"
4 | import {getAuth, onAuthStateChanged} from "firebase/auth"
5 | const Header = () => {
6 | const [pageState, setPageState] = useState("Sign in")
7 | /*useLocation returns the current location object, which represents the current URL in web browsers. */
8 | const location = useLocation();
9 |
10 | /*useNavigate returns an imperative method for changing the location. Used by s, but may also be used by other elements to change the location. */
11 | const navigate = useNavigate()
12 | const auth = getAuth();
13 | /* added useEffect to track the changes in auth */
14 | useEffect(() => {
15 | onAuthStateChanged(auth, (user) =>{
16 | if(user){
17 | setPageState('Profile')
18 | }else{
19 | setPageState('Sign in')
20 | }
21 | } )
22 | }, [auth]);
23 |
24 | function pathMatchRoute(route){
25 | if(route === location.pathname){
26 | return true;
27 | }
28 | }
29 |
30 | return (
31 |
32 |
33 |
34 | navigate("/")}>
37 |
38 |
39 | {/* Items of menu where I used useLocation to returns the current location of each element on menu and the useNavigation to returns the changing of each element on menu */}
40 |
41 |
42 | {/* if delete exists, it is going to have the trash icon, and this is going to call the function */}
43 | {onDelete && (
44 | onDelete(listing.id)}/>
46 |
47 | )}
48 | {onEdit && (
49 | onEdit(listing.id)}/>
51 |
52 | )}
53 |
;
54 | }
--------------------------------------------------------------------------------
/src/pages/ForgotPassword.jsx:
--------------------------------------------------------------------------------
1 | import { getAuth, sendPasswordResetEmail } from 'firebase/auth';
2 | import React from 'react';
3 | import {useState} from 'react';
4 | import { Link } from 'react-router-dom';
5 | import { toast } from 'react-toastify';
6 | import OAuth from '../components/OAuth';
7 |
8 |
9 | const ForgotPassword = () => {
10 | const [email, setEmail] = useState("");
11 | function onChange(e){
12 | setEmail(e.target.value);
13 | };
14 |
15 | async function onSubmit(e){
16 | /* Prevent to refresh the page */
17 | e.preventDefault();
18 | try {
19 | /* Send the password */
20 | const auth = getAuth()
21 | await sendPasswordResetEmail(auth, email)
22 | /* if it was successful */
23 | toast.success("Email was sent")
24 | } catch (error) {
25 | toast.error("Could not resend password")
26 | }
27 | }
28 | return (
29 |
30 |
There are no current {" "} {params.categoryName === "rent" ? "Places for rent" : "Places for sell"}
108 | )}
109 |
110 | );
111 | }
--------------------------------------------------------------------------------
/src/pages/SignIn.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import {useState} from 'react';
3 | import {AiFillEyeInvisible, AiFillEye} from 'react-icons/ai'
4 | import { Link, useNavigate } from 'react-router-dom';
5 | import OAuth from '../components/OAuth';
6 | import { signInWithEmailAndPassword, getAuth } from 'firebase/auth';
7 | import {toast} from "react-toastify"
8 |
9 |
10 | const SignIn = () => {
11 | /* Created a hook for show password */
12 | const [showPassword, setShowPassword] = useState(false);
13 |
14 | const navigate = useNavigate();
15 | /* created a hook for the formdata */
16 | const [formData, setFormData] = useState({
17 | /* Initial value was an empty string */
18 | email: "",
19 | password: "",
20 | });
21 | const {email, password} = formData;
22 | /* function for any change happens to the forms we are going to get it gets and puts it inside of the formdata state */
23 | function onChange(e){
24 | setFormData((prevState)=> ({
25 | ...prevState,
26 | [e.target.id]: e.target.value,
27 | }));
28 | }
29 |
30 | async function onSubmit(e){
31 | /* Prevent to refresh the page */
32 | e.preventDefault()
33 | try {
34 | const auth = getAuth()
35 | const userCredential = await signInWithEmailAndPassword(auth, email, password)
36 | if(userCredential.user){
37 | navigate("/")
38 | }
39 | } catch (error) {
40 | toast.error("Bad user credentials")
41 | }
42 | }
43 | return (
44 |
45 | {/* Just added h1 for the title sign up */}
46 |
Sign In
47 |
48 |
49 | {/* Adding image */}
50 |
54 |
55 | {/* Adding a form with two inputs
56 | 1. For the email
57 | 2. For the password
58 | So each input has a value onChange for tracking the changes inside our input */}
59 |
159 | );
160 | }
161 |
162 | export default Home;
163 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # React.js & Firebase Project - ReactJS 18, Firebase 9 Project
2 |
3 |
4 | React.js and Firebase portfolio project. Build Realtor (Real estate) clone using React js 18, Firebase 9, Tailwind CSS 3
5 |
6 | 
7 |
8 | ### Project Description
9 | - Realtor.com is a real estate listings website operated by the News Corp subsidiary Move, Inc. and based in Santa Clara, California. It is the second most visited real estate listings website in the United States as of 2021, with over 100 million monthly active users. This project focus on build a realtor clone application.
10 |
11 | ### Stacks
12 | - Build Realtor (Real estate) clone using React js 18, Firebase 9, Tailwind CSS 3, and React router 6.
13 | - Create a React js project from scratch
14 | - Use Firebase auth for complete authentication
15 | - Use Firebase Firestore to store and fetch data
16 | - Sign up/in the users using username/password and Google oAuth using Firebase auth
17 | - Add forgot password functionality using Firebase auth
18 | - Work with latest versions like React js 18, Firebase 9 and Tailwind CSS 3
19 | - CRUD operations including create, read, update and delete using Firebase Firestore
20 | - React router version 6 (latest version) to create routes, get the params and redirect
21 | - Create pages and routes in a react project
22 | - React toastify to create nice notifications
23 | - Create private route and custom hook for protecting the user profile page
24 | - Spinner and loader
25 | - React event listeners like onChange and onSubmit
26 | - Reusable component such as listing cards
27 | - Create an image slider using Swiper js latest version
28 | - Add map to the page using leaflet and react leaflet packages
29 | - Deploy to vercel
30 | - Google geolocation api and how to convert address to latitude and longitude
31 | - Tailwind CSS 3 to style a react project
32 | - useEffect and useState react hooks
33 |
34 |
35 | ### Screens
36 |
37 |
38 | 
39 |
40 | 
41 |
42 | 
43 |
44 | ### Mobile
45 |
46 |
47 | 
48 |
49 | 
50 |
51 |
52 | # Getting Started with Create React App
53 |
54 | This project was bootstrapped with [Create React App](https://github.com/facebook/create-react-app).
55 |
56 | ## Available Scripts
57 |
58 | In the project directory, you can run:
59 |
60 | ### `npm start`
61 |
62 | Runs the app in the development mode.\
63 | Open [http://localhost:3000](http://localhost:3000) to view it in your browser.
64 |
65 | The page will reload when you make changes.\
66 | You may also see any lint errors in the console.
67 |
68 | ### `npm test`
69 |
70 | Launches the test runner in the interactive watch mode.\
71 | See the section about [running tests](https://facebook.github.io/create-react-app/docs/running-tests) for more information.
72 |
73 | ### `npm run build`
74 |
75 | Builds the app for production to the `build` folder.\
76 | It correctly bundles React in production mode and optimizes the build for the best performance.
77 |
78 | The build is minified and the filenames include the hashes.\
79 | Your app is ready to be deployed!
80 |
81 | See the section about [deployment](https://facebook.github.io/create-react-app/docs/deployment) for more information.
82 |
83 | ### `npm run eject`
84 |
85 | **Note: this is a one-way operation. Once you `eject`, you can't go back!**
86 |
87 | If you aren't satisfied with the build tool and configuration choices, you can `eject` at any time. This command will remove the single build dependency from your project.
88 |
89 | Instead, it will copy all the configuration files and the transitive dependencies (webpack, Babel, ESLint, etc) right into your project so you have full control over them. All of the commands except `eject` will still work, but they will point to the copied scripts so you can tweak them. At this point you're on your own.
90 |
91 | You don't have to ever use `eject`. The curated feature set is suitable for small and middle deployments, and you shouldn't feel obligated to use this feature. However we understand that this tool wouldn't be useful if you couldn't customize it when you are ready for it.
92 |
93 | ## Learn More
94 |
95 | You can learn more in the [Create React App documentation](https://facebook.github.io/create-react-app/docs/getting-started).
96 |
97 | To learn React, check out the [React documentation](https://reactjs.org/).
98 |
99 | ### Code Splitting
100 |
101 | This section has moved here: [https://facebook.github.io/create-react-app/docs/code-splitting](https://facebook.github.io/create-react-app/docs/code-splitting)
102 |
103 | ### Analyzing the Bundle Size
104 |
105 | This section has moved here: [https://facebook.github.io/create-react-app/docs/analyzing-the-bundle-size](https://facebook.github.io/create-react-app/docs/analyzing-the-bundle-size)
106 |
107 | ### Making a Progressive Web App
108 |
109 | This section has moved here: [https://facebook.github.io/create-react-app/docs/making-a-progressive-web-app](https://facebook.github.io/create-react-app/docs/making-a-progressive-web-app)
110 |
111 | ### Advanced Configuration
112 |
113 | This section has moved here: [https://facebook.github.io/create-react-app/docs/advanced-configuration](https://facebook.github.io/create-react-app/docs/advanced-configuration)
114 |
115 | ### Deployment
116 |
117 | This section has moved here: [https://facebook.github.io/create-react-app/docs/deployment](https://facebook.github.io/create-react-app/docs/deployment)
118 |
119 | ### `npm run build` fails to minify
120 |
121 | This section has moved here: [https://facebook.github.io/create-react-app/docs/troubleshooting#npm-run-build-fails-to-minify](https://facebook.github.io/create-react-app/docs/troubleshooting#npm-run-build-fails-to-minify)
122 |
--------------------------------------------------------------------------------
/src/pages/SignUp.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import {useState} from 'react';
3 | import {AiFillEyeInvisible, AiFillEye} from 'react-icons/ai'
4 | import { Link } from 'react-router-dom';
5 | import OAuth from '../components/OAuth';
6 | import {getAuth, createUserWithEmailAndPassword, updateProfile} from "firebase/auth";
7 | import {db} from "../firebase"
8 | import { doc, serverTimestamp, setDoc } from 'firebase/firestore';
9 | import { useNavigate } from 'react-router-dom';
10 | import { toast } from 'react-toastify';
11 |
12 | const SignUp = () => {
13 | /* Created a hook for show password */
14 | const [showPassword, setShowPassword] = useState(false);
15 | /* created a hook for the formdata */
16 | const [formData, setFormData] = useState({
17 | /* Initial value was an empty string */
18 | name:"",
19 | email: "",
20 | password: "",
21 | });
22 | const {name, email, password} = formData;
23 | /* function for any change happens to the forms we are going to get it gets and puts it inside of the formdata state */
24 | const navigate = useNavigate()
25 | function onChange(e){
26 | setFormData((prevState)=> ({
27 | ...prevState,
28 | [e.target.id]: e.target.value,
29 | }));
30 | }
31 | async function onSubmit(e){
32 | e.preventDefault();
33 |
34 | try {
35 | const auth = getAuth()
36 | const userCredential = await createUserWithEmailAndPassword(auth,
37 | email,
38 | password);
39 |
40 | updateProfile(auth.currentUser, {
41 | displayName : name,
42 | })
43 | const user = userCredential.user
44 | const formDataCopy = {...formData}
45 | delete formDataCopy.password
46 | formDataCopy.timestamp = serverTimestamp();
47 |
48 | await setDoc(doc(db, "users", user.uid), formDataCopy)
49 | toast.success("Sign up was successful")
50 | navigate("/");
51 | } catch (error) {
52 | toast.error("Something went wrong with the registration")
53 | console.log(error)
54 | }
55 | }
56 | return (
57 |
58 | {/* Just added h1 for the title sign up */}
59 |
Sign Up
60 |
61 |
62 | {/* Adding image */}
63 |
67 |
68 |
69 | {/* Adding a form with three inputs
70 | 1. For the name
71 | 2. For the email
72 | 3. For the password
73 | So each input has a value onChange for tracking the changes inside our input */}
74 |
131 |
132 |
127 | {/* the condition if the person is visiting this page should not be the owner of the listing page */}
128 | {listing.userRef !== auth.currentUser?.uid && !contactLandlord && (
129 |