add listings added

This commit is contained in:
QkoSad
2022-12-02 08:14:14 +02:00
parent 8cc369bfe6
commit 5c96af8b6c
9 changed files with 574 additions and 11 deletions
+5
View File
@@ -9,6 +9,8 @@ import ForgotPassword from "./pages/ForgotPassword";
import Offers from "./pages/Offers";
import Signin from "./pages/Signin";
import Signup from "./pages/Signup";
import Category from "./pages/Category";
import CreateLising from "./pages/CreateListing";
function App() {
return (
@@ -17,12 +19,15 @@ function App() {
<Routes>
<Route path="/" element={<Explore />} />
<Route path="/offers" element={<Offers />} />
<Route path="/category/:categoryName" element={<Category />} />
<Route path="/profile" element={<PrivateRoute />}>
<Route path="/profile" element={<Profile />} />
</Route>
<Route path="/sign-in" element={<Signin />} />
<Route path="/sign-up" element={<Signup />} />
<Route path="/forgot-password" element={<ForgotPassword />} />
<Route path="/create-listing" element={<CreateLising />} />
</Routes>
<Navbar />
</Router>
+58
View File
@@ -0,0 +1,58 @@
import { Link } from "react-router-dom";
import { ReactComponent as DeleteIcon } from "../assets/svg/deleteIcon.svg";
import bedIcon from "../assets/svg/bedIcon.svg";
import bathtubIcon from "../assets/svg/bathtubIcon.svg";
function ListingItem({ listing, id, onDelete }) {
return (
<li className="categoryListing">
<Link
to={`/category/${listing.type}/${id}`}
className="categoryListingLink"
>
<img
src={listing.imgUrls[0]}
alt={listing.name}
className="categoryListingImg"
/>
<div className="categoryListingDetails">
<p className="categoryListingLocation">{listing.location}</p>
<p className="categoryListingName">{listing.name}</p>
<p className="categoryListingPrice">
$
{listing.offer
? listing.discountedPrice
.toString()
.replace(/\B(?=(\d{3})+(?!\d))/g, ",")
: listing.regularPrice
.toString()
.replace(/\B(?=(\d{3})+(?!\d))/g, ",")}
{listing.type === "rent" && " / Month"}
</p>
<div className="categoryListingInfoDiv">
<img src={bedIcon} alt="bed" />
<p className="categoryListingInfoText">
{listing.bedrooms > 1
? `${listing.bedrooms} Bedrooms`
: "1 Bedrooms"}
</p>
<img src={bathtubIcon} alt="bath" />
<p className="categoryListingInfoText">
{listing.bathrooms > 1
? `${listing.bathrooms} Bedrooms`
: "1 Bathrooms"}
</p>
</div>
</div>
</Link>
{onDelete && (
<DeleteIcon
className="removeIcon"
fill="rgb(231,76,60)"
onClick={() => onDelete(listing.id, listing.name)}
/>
)}
</li>
);
}
export default ListingItem;
+1 -1
View File
@@ -16,7 +16,7 @@ function Navbar() {
<footer className="navbar">
<nav className="navbarNan">
<ul className="navbarListItems">
<li className="navbarListItems" onClick={() => navigate("/explore")}>
<li className="navbarListItems" onClick={() => navigate("/")}>
<ExploreIcon
fill={pathMatchRoute("/") ? "#2c2c2c" : "#8f8f8f"}
width="36px"
-1
View File
@@ -8,7 +8,6 @@ const PrivateRoute = () => {
if (checkingStatus) {
return <Spinner/>
}
console.log(loggedIn)
return loggedIn ? <Outlet /> : <Navigate to="/sign-in" />;
};
export default PrivateRoute;
+86
View File
@@ -0,0 +1,86 @@
import { useEffect, useState } from "react";
import { useParams } from "react-router-dom";
import {
collection,
getDocs,
query,
where,
orderBy,
limit,
startAfter,
} from "firebase/firestore";
import { db } from "../firebase.config";
import { toast } from "react-toastify";
import Spinner from "../components/Spinner";
import { async } from "@firebase/util";
import Listing from "../components/ListingItem";
import ListingItem from "../components/ListingItem";
function Category() {
const [listings, setListings] = useState(null);
const [loading, setLoading] = useState(true);
const params = useParams();
useEffect(() => {
const fetchListings = async () => {
try {
const listingsRef = collection(db, "listings");
const q = query(
listingsRef,
where("type", "==", params.categoryName),
orderBy("timestamp", "desc"),
limit(10)
);
const querySnap = await getDocs(q);
let listings = [];
querySnap.forEach((doc) => {
return listings.push({
id: doc.id,
data: doc.data(),
});
});
setListings(listings);
setLoading(false);
} catch (error) {
console.log(error)
toast.error("could not fetch listings");
}
};
fetchListings();
}, [params.categoryName]);
return (
<div className="category">
<header>
<p className="pageHeader">
{params.categoryName === "rent"
? "Places for rent"
: "Places for sale"}
</p>
</header>
{loading ? (
<Spinner />
) : listings && listings.length > 0 ? (
<>
<main>
<ul className="categoryListings">
{listings.map((listing) => (
<ListingItem
listing={listing.data}
id={listing.id}
key={listing.id}
/>
))}
</ul>
</main>
</>
) : (
<p>No listings for {params.categoryName}</p>
)}
</div>
);
}
export default Category;
+317
View File
@@ -0,0 +1,317 @@
import { useState, useRef, useEffect } from "react";
import { getAuth, onAuthStateChanged } from "firebase/auth";
import { useNavigate } from "react-router-dom";
import Spinner from "../components/Spinner";
function CreateLising() {
const [geolocationEnabled, setGeolocationEnabled] = useState(true);
const [loading, setLoading] = useState(false);
const [formData, setFormData] = useState({
type: "rent",
name: "",
bedrooms: 1,
bathrooms: 1,
parking: false,
furnished: false,
address: "",
offer: false,
regularPrice: 0,
discountedPrice: 0,
images: {},
latittude: 0,
longitude: 0,
});
const {
type,
name,
bedrooms,
bathrooms,
parking,
furnished,
address,
offer,
regularPrice,
discountedPrice,
images,
latittude,
longitude,
} = formData;
const auth = getAuth();
const navigate = useNavigate();
const isMounted = useRef(true);
useEffect(() => {
if (isMounted) {
onAuthStateChanged(auth, (user) => {
if (user) {
setFormData({ ...formData, userRef: user.uid });
} else {
navigate("/sign-in");
}
});
}
return () => {
isMounted.current = false;
};
}, [isMounted]);
const onSubmit = (e) => {
e.prevent.default();
};
const onMutate = (e) => {
let boolean = null;
if (e.target.value === "false") {
boolean = false;
}
if (e.target.value === "true") {
boolean = true;
}
if (e.target.files) {
setFormData((prevState) => ({
...prevState,
image: e.target.files,
}));
}
if (!e.target.files) {
setFormData((prevState) => ({
...prevState,
[e.target.id]: boolean ?? e.target.value,
}));
}
if (loading) {
return <Spinner />;
}
};
return (
<div className="profile">
<header>
<p className="pageHeader">Create a listing</p>
</header>
<main>
<form onSubmit={onSubmit}>
<label className="formLabel">Sell / Rent</label>
<div className="formButtons">
<button
type="button"
className={type === "sale" ? "formButtonActive" : "formButton"}
id="type"
value="sale"
onClick={onMutate}
>
Sell
</button>
<button
type="button"
className={type === "rent" ? "formButtonActive" : "formButton"}
id="type"
value="rent"
onClick={onMutate}
>
Rent
</button>
</div>
<label className="formLabel">Sell / Rent</label>
<input
className="formInputName"
type="text"
id="name"
value={name}
onChange={onMutate}
maxLength="32"
minLength="10"
required
/>
<div className="formRooms fles">
<div>
<label className="formLabel">Bedrooms</label>
<input
className="formInputSmall"
type="number"
id="bedrooms"
value={bedrooms}
onChange={onMutate}
min="1"
max="50"
required
/>
</div>
<div>
<label className="formLabel">Bathrooms</label>
<input
className="formInputSmall"
type="number"
id="bathrooms"
value={bathrooms}
onChange={onMutate}
min="1"
max="50"
required
/>
</div>
</div>
<label className="formLabel">Parking spot</label>
<div className="formButtons">
<button
className={parking ? "formButtonActive" : "formButton"}
type="button"
id="parking"
value={true}
onClick={onMutate}
min="1"
max="50"
>
Yes
</button>
<button
className={
!parking && parking !== null ? "formButtonActive" : "formButton"
}
type="button"
id="parking"
value={false}
onClick={onMutate}
>
No
</button>
</div>
<label className="formLabel">Furnished</label>
<div className="formButtons">
<button
className={furnished ? "formButtonActive" : "formButton"}
type="button"
id="furnished"
value={true}
onClick={onMutate}
>
Yes
</button>
<button
className={
!furnished && furnished !== null
? "formButtonActive"
: "formButton"
}
type="button"
id="furnished"
value={false}
onClick={onMutate}
>
No
</button>
</div>
<textarea
className="formInputAddress"
type="text"
id="address"
value={address}
onChange={onMutate}
required
/>
{!geolocationEnabled && (
<div className="formLatLng fles">
<div>
<label className="formLabel">Latitude</label>
<input
className="formInputSmall"
type="number"
id="latittude"
value={latittude}
onChange={onMutate}
required
/>
</div>
<div>
<label className="formLabel">Longitude</label>
<input
className="formInputSmall"
type="number"
id="longitude"
value={longitude}
onChange={onMutate}
required
/>
</div>
</div>
)}
<label className='formLabel'>Offer</label>
<div className='formButtons'>
<button
className={offer ? 'formButtonActive' : 'formButton'}
type='button'
id='offer'
value={true}
onClick={onMutate}
>
Yes
</button>
<button
className={
!offer && offer !== null ? 'formButtonActive' : 'formButton'
}
type='button'
id='offer'
value={false}
onClick={onMutate}
>
No
</button>
</div>
<label className="formLabel"> Regular Price</label>
<div className="formPriceDiv">
<input
className="formInputSmall"
type="number"
id="regularPrice"
value={regularPrice}
onChange={onMutate}
min="50"
max="750000000"
required
/>
{formData.type === "rent" && (
<p className="formPriceText">$ / Month</p>
)}
</div>
{offer && (
<>
<label className="formLabel">Discounted Price</label>
<input
className="formInputName"
type="number"
id="discountedPrice"
value={discountedPrice}
onChange={onMutate}
min="50"
max="750000000"
required={offer}
/>
</>
)}
<label className="formLabel">Discounted Price</label>
<p className="imagesInfo">
The first image will be the cover (max 6).
</p>
<input
className="formInputFile"
type="file"
id="image"
onChange={onMutate}
max="6"
accept=".jpg,.png,.jpeg"
multiple
required
/>
<button className="primaryButton createListingButton">
Create Listing
</button>
</form>
</main>
</div>
);
}
export default CreateLising;
+22 -4
View File
@@ -1,9 +1,27 @@
import {Link} from 'react-router-dom'
import rentCategoryImage from '../assets/jpg/rentCategoryImage.jpg'
import sellCategoryImage from '../assets/jpg/sellCategoryImage.jpg'
function Explore (){
return (
<>
<h1>My app</h1>
</>
<div className='export'>
<header>
<p className='pageHeader'>Explore</p>
</header>
<main>
{/*Slider*/}
<p className='exploreCategoryHeading'>Categories</p>
<div className='exploreCategories'>
<Link to='/category/rent'>
<img src={rentCategoryImage} alt='rent' className='exploreCategoryImg'/>
</Link>
<p className='exploreCategoryName'>Places for rent</p>
<Link to='/category/sale'>
<img src={sellCategoryImage} alt='sell' className='exploreCategoryImg'/>
</Link>
<p className='exploreCategoryName'>Places for sale</p>
</div>
</main>
</div>
);
}
+78 -5
View File
@@ -1,9 +1,82 @@
import { useEffect, useState } from "react";
import { useParams } from "react-router-dom";
import {
collection,
getDocs,
query,
where,
orderBy,
limit,
startAfter,
} from "firebase/firestore";
import { db } from "../firebase.config";
import { toast } from "react-toastify";
import Spinner from "../components/Spinner";
import { async } from "@firebase/util";
import Listing from "../components/ListingItem";
import ListingItem from "../components/ListingItem";
function Offers() {
const [listings, setListings] = useState(null);
const [loading, setLoading] = useState(true);
const params = useParams();
useEffect(() => {
const fetchListings = async () => {
try {
const listingsRef = collection(db, "listings");
const q = query(
listingsRef,
where("offer", "==", true),
orderBy("timestamp", "desc"),
limit(10)
);
const querySnap = await getDocs(q);
let listings = [];
querySnap.forEach((doc) => {
return listings.push({
id: doc.id,
data: doc.data(),
});
});
setListings(listings);
setLoading(false);
} catch (error) {
console.log(error);
toast.error("could not fetch listings");
}
};
fetchListings();
}, []);
function Offers (){
return (
<>
<h1>My app</h1>
</>
<div className="category">
<header>
<p className="pageHeader">Offers</p>
</header>
{loading ? (
<Spinner />
) : listings && listings.length > 0 ? (
<>
<main>
<ul className="categoryListings">
{listings.map((listing) => (
<ListingItem
listing={listing.data}
id={listing.id}
key={listing.id}
/>
))}
</ul>
</main>
</>
) : (
<p>There are no current Offers</p>
)}
</div>
);
}
export default Offers
export default Offers;
+7
View File
@@ -1,3 +1,5 @@
import arrowRight from '../assets/svg/keyboardArrowRightIcon.svg'
import homeIcon from '../assets/svg/homeIcon.svg'
import { toast } from "react-toastify";
import { useEffect, useState } from "react";
import { getAuth, updateProfile } from "firebase/auth";
@@ -82,6 +84,11 @@ function Profile() {
/>
</form>
</div>
<Link to='/create-listing' className='createListing'>
<img src={homeIcon} alt='home'/>
<p>Sell or rent your home</p>
<img src={arrowRight} alt='arrowRight'/>
</Link>
</main>
</div>
);