-
-
Save Daltonic/8ef4a09cb82378fd778bd94ada1311a2 to your computer and use it in GitHub Desktop.
DappBnb Project Room view
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { useEffect, useState } from 'react' | |
import { FaEthereum } from 'react-icons/fa' | |
import { CiEdit } from 'react-icons/ci' | |
import { FiCalendar } from 'react-icons/fi' | |
import { MdDeleteOutline } from 'react-icons/md' | |
import { BiBookOpen, BiMedal } from 'react-icons/bi' | |
import { BsChatLeft } from 'react-icons/bs' | |
import Identicon from 'react-identicons' | |
import { Link, useParams, useNavigate } from 'react-router-dom' | |
import DatePicker from 'react-datepicker' | |
import { useGlobalState, setGlobalState, truncate } from '../store' | |
import moment from 'moment' | |
import AddReview from '../components/AddReview' | |
import { toast } from 'react-toastify' | |
import { loginWithCometChat, signUpWithCometChat } from '../services/Chat' | |
import { | |
deleteAppartment, | |
loadAppartment, | |
loadReviews, | |
loadAppartments, | |
appartmentBooking, | |
getUnavailableDates, | |
} from '../Blockchain.services' | |
const Room = () => { | |
const { id } = useParams() | |
const [appartment] = useGlobalState('appartment') | |
const [reviews] = useGlobalState('reviews') | |
const [booked] = useGlobalState('booked') | |
const handleReviewOpen = () => { | |
setGlobalState('reviewModal', 'scale-100') | |
} | |
useEffect(async () => { | |
await loadAppartment(id) | |
await loadReviews(id) | |
}, []) | |
return ( | |
<> | |
<div className="py-8 px-10 sm:px-20 md:px-32 space-y-8"> | |
<RoomHeader name={appartment?.name} rooms={appartment?.rooms} /> | |
<RoomGrid | |
first={appartment?.images[0]} | |
second={appartment?.images[1]} | |
third={appartment?.images[2]} | |
forth={appartment?.images[3]} | |
fifth={appartment?.images[4]} | |
/> | |
<RoomDescription description={appartment?.description} /> | |
<RoomCalendar price={appartment?.price} /> | |
<RoomButtons id={appartment?.id} owner={appartment?.owner} /> | |
<div className="flex flex-col justify-between flex-wrap space-y-2"> | |
<h1 className="text-xl font-semibold">Reviews</h1> | |
<div> | |
{reviews.length > 0 | |
? reviews.map((review, index) => ( | |
<RoomReview key={index} review={review} /> | |
)) | |
: 'No reviews yet!'} | |
</div> | |
</div> | |
{booked ? ( | |
<p | |
className="underline mt-11 cursor-pointer hover:text-blue-700" | |
onClick={handleReviewOpen} | |
> | |
Drop your review | |
</p> | |
) : null} | |
</div> | |
<AddReview /> | |
</> | |
) | |
} | |
const RoomHeader = ({ name, rooms }) => { | |
return ( | |
<div> | |
<h1 className="text-3xl font-semibold">{name}</h1> | |
<div className="flex justify-between"> | |
<div className="flex items-center mt-2 space-x-2 text-lg text-slate-500"> | |
<span> | |
{rooms} {rooms == 1 ? 'room' : 'rooms'} | |
</span> | |
</div> | |
</div> | |
</div> | |
) | |
} | |
const RoomButtons = ({ id, owner }) => { | |
const [currentUser] = useGlobalState('currentUser') | |
const [connectedAccount] = useGlobalState('connectedAccount') | |
const navigate = useNavigate() | |
const handleDelete = async () => { | |
if (confirm('Are you sure you want to delete?')) { | |
await toast.promise( | |
new Promise(async (resolve, reject) => { | |
await deleteAppartment(id) | |
.then(async () => { | |
navigate('/') | |
await loadAppartments() | |
resolve() | |
}) | |
.catch(() => reject()) | |
}), | |
{ | |
pending: 'Approve transaction...', | |
success: 'apartment deleted successfully 👌', | |
error: 'Encountered error 🤯', | |
} | |
) | |
} | |
} | |
const handleSignUp = async () => { | |
await toast.promise( | |
new Promise(async (resolve, reject) => { | |
await signUpWithCometChat() | |
.then(() => resolve()) | |
.catch((error) => reject(error)) | |
}), | |
{ | |
pending: 'Registering...', | |
success: 'Account created, please login 👌', | |
error: 'Encountered error 🤯', | |
} | |
) | |
} | |
const handleLogin = async () => { | |
await toast.promise( | |
new Promise(async (resolve, reject) => { | |
await loginWithCometChat() | |
.then(async (user) => { | |
setGlobalState('currentUser', user) | |
resolve(user) | |
}) | |
.catch((error) => reject(error)) | |
}), | |
{ | |
pending: 'Authenticating...', | |
success: 'Logged in successfully 👌', | |
error: 'Encountered error 🤯', | |
} | |
) | |
} | |
return ( | |
<div className="flex justify-start items-center space-x-3 border-b-2 border-b-slate-200 pb-6"> | |
{currentUser && currentUser.status != 'offline' ? ( | |
<Link | |
to={`/chats/${owner}`} | |
className="p-2 rounded-md shadow-lg border-[0.1px] | |
border-gray-300 flex justify-start items-center space-x-1 | |
bg-white hover:bg-gray-100" | |
> | |
<BsChatLeft size={15} className="text-pink-500" /> | |
<small>Chats</small> | |
</Link> | |
) : ( | |
<> | |
<button | |
className="p-2 rounded-md shadow-lg border-[0.1px] | |
border-gray-300 flex justify-start items-center space-x-1 | |
bg-white hover:bg-gray-100" | |
onClick={handleSignUp} | |
> | |
<small>Sign up</small> | |
</button> | |
<button | |
className="p-2 rounded-md shadow-lg border-[0.1px] | |
border-gray-300 flex justify-start items-center space-x-1 | |
bg-white hover:bg-gray-100" | |
onClick={handleLogin} | |
> | |
<small>Login to chat</small> | |
</button> | |
</> | |
)} | |
{connectedAccount == owner ? ( | |
<> | |
<Link | |
to={'/editRoom/' + id} | |
className="p-2 rounded-md shadow-lg border-[0.1px] | |
border-gray-500 flex justify-start items-center space-x-1 | |
bg-gray-500 hover:bg-transparent hover:text-gray-500 text-white" | |
> | |
<CiEdit size={15} /> | |
<small>Edit</small> | |
</Link> | |
<button | |
className="p-2 rounded-md shadow-lg border-[0.1px] | |
border-pink-500 flex justify-start items-center space-x-1 | |
bg-pink-500 hover:bg-transparent hover:text-pink-500 text-white" | |
onClick={handleDelete} | |
> | |
<MdDeleteOutline size={15} /> | |
<small>Delete</small> | |
</button> | |
</> | |
) : null} | |
</div> | |
) | |
} | |
const RoomGrid = ({ first, second, third, forth, fifth }) => { | |
return ( | |
<div className="mt-8 h-[32rem] flex rounded-2xl overflow-hidden"> | |
<div className="md:w-1/2 w-full overflow-hidden"> | |
<img className="object-cover w-full h-full" src={first} /> | |
</div> | |
<div className="w-1/2 md:flex hidden flex-wrap"> | |
<img src={second} className="object-cover w-1/2 h-64 pl-2 pb-1 pr-1" /> | |
<img src={third} alt="" className="object-cover w-1/2 h-64 pl-1 pb-1" /> | |
<img src={forth} className="object-cover w-1/2 h-64 pt-1 pl-2 pr-1" /> | |
<img | |
src={fifth} | |
className="object-cover sm:w-2/5 md:w-1/2 h-64 pl-1 pt-1" | |
/> | |
</div> | |
</div> | |
) | |
} | |
const RoomDescription = ({ description }) => { | |
return ( | |
<div className="py-5 border-b-2 border-b-slate-200 space-y-4"> | |
<h1 className="text-xl font-semibold">Description</h1> | |
<p className="text-slate-500 text-lg w-full sm:w-4/5">{description}</p> | |
<div className=" flex space-x-4 "> | |
<BiBookOpen className="text-4xl" /> | |
<div> | |
<h1 className="text-xl font-semibold">Featured in</h1> | |
<p className="cursor-pointer">Condé Nast Traveler, June 2023</p> | |
</div> | |
</div> | |
<div className=" flex space-x-4"> | |
<BiMedal className="text-4xl" /> | |
<div> | |
<h1 className="text-xl font-semibold"> | |
Vittorio Emanuele is a Superhost | |
</h1> | |
<p> | |
Superhosts are experienced, highly rated hosts who are committed to | |
providing great stays for guests. | |
</p> | |
</div> | |
</div> | |
<div className=" flex space-x-4"> | |
<FiCalendar className="text-4xl" /> | |
<div> | |
<h1 className="text-xl font-semibold"> | |
Free cancellation before Oct 17. | |
</h1> | |
</div> | |
</div> | |
</div> | |
) | |
} | |
const RoomCalendar = ({ price }) => { | |
const [checkInDate, setCheckInDate] = useState(null) | |
const [checkOutDate, setCheckOutDate] = useState(null) | |
const { id } = useParams() | |
const [timestamps] = useGlobalState('timestamps') | |
useEffect(async () => await getUnavailableDates(id)) | |
const handleCheckInDateChange = (date) => { | |
setCheckInDate(date) | |
} | |
const handleCheckOutDateChange = (date) => { | |
setCheckOutDate(date) | |
} | |
const handleSubmit = async (e) => { | |
e.preventDefault() | |
if (!checkInDate || !checkOutDate) return | |
const start = moment(checkInDate) | |
const end = moment(checkOutDate) | |
const timestampArray = [] | |
while (start <= end) { | |
timestampArray.push(start.valueOf()) | |
start.add(1, 'days') | |
} | |
const params = { | |
id, | |
datesArray: timestampArray, | |
amount: price * timestampArray.length, | |
} | |
await toast.promise( | |
new Promise(async (resolve, reject) => { | |
await appartmentBooking(params) | |
.then(async () => { | |
resetForm() | |
resolve() | |
}) | |
.catch(() => reject()) | |
}), | |
{ | |
pending: 'Approve transaction...', | |
success: 'apartment booked successfully 👌', | |
error: 'Encountered error 🤯', | |
} | |
) | |
} | |
const resetForm = () => { | |
setCheckInDate(null) | |
setCheckOutDate(null) | |
} | |
return ( | |
<form | |
onSubmit={handleSubmit} | |
className="sm:w-[25rem] border-[0.1px] p-6 | |
border-gray-400 rounded-lg shadow-lg flex flex-col | |
space-y-4" | |
> | |
<div className="flex justify-between"> | |
<div className="flex justify-center items-center"> | |
<FaEthereum className="text-lg text-gray-500" /> | |
<span className="text-lg text-gray-500"> | |
{price} <small>per night</small> | |
</span> | |
</div> | |
</div> | |
<DatePicker | |
id="checkInDate" | |
selected={checkInDate} | |
onChange={handleCheckInDateChange} | |
placeholderText={'Check In'} | |
dateFormat="yyyy-MM-dd" | |
minDate={new Date()} | |
excludeDates={timestamps} | |
required | |
className="rounded-lg w-full" | |
/> | |
<DatePicker | |
id="checkOutDate" | |
selected={checkOutDate} | |
onChange={handleCheckOutDateChange} | |
placeholderText={'Check out'} | |
dateFormat="yyyy-MM-dd" | |
minDate={checkInDate} | |
excludeDates={timestamps} | |
required | |
className="rounded-lg w-full" | |
/> | |
<button | |
className="p-2 border-none bg-gradient-to-l from-pink-600 | |
to-gray-600 text-white w-full rounded-md focus:outline-none | |
focus:ring-0" | |
> | |
Book | |
</button> | |
<Link to={`/bookings/${id}`} className="text-pink-500"> | |
Check your bookings | |
</Link> | |
</form> | |
) | |
} | |
const RoomReview = ({ review }) => { | |
return ( | |
<div className="w-1/2 pr-5 space-y-2"> | |
<div className="pt-2 flex items-center space-x-2"> | |
<Identicon | |
string={review.owner} | |
size={20} | |
className="rounded-full shadow-gray-500 shadow-sm" | |
/> | |
<div className="flex justify-start items-center space-x-2"> | |
<p className="text-md font-semibold"> | |
{truncate(review.owner, 4, 4, 11)}{' '} | |
</p> | |
<p className="text-slate-500 text-sm">{review.timestamp}</p> | |
</div> | |
</div> | |
<p className="text-slate-500 text-sm w-full sm:w-4/5"> | |
{review.reviewText} | |
</p> | |
</div> | |
) | |
} | |
export default Room |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment