├── CODEOWNERS
├── frontend
├── .npmrc
├── postcss.config.js
├── public
│ └── airbnb-logo.png
├── constants
│ ├── index.js
│ └── airbnb.json
├── styles
│ └── globals.css
├── utils
│ ├── index.js
│ └── string.js
├── tailwind.config.js
├── next.config.js
├── pages
│ ├── _app.js
│ └── index.js
├── context
│ └── WalletConnectionProvider.js
├── components
│ ├── Listing
│ │ ├── Listings.js
│ │ ├── ListingItem.js
│ │ ├── ReserveListingModal.js
│ │ ├── AddListingModal.js
│ │ └── EditListingModal.js
│ ├── Footer.js
│ ├── FilterMenu.js
│ └── Header.js
├── package.json
├── data
│ ├── airbnb.js
│ └── listings.js
├── README.md
└── hooks
│ └── useAirbnb.js
├── package.json
├── solana-solution
├── constant.rs
├── error.rs
├── states.rs
└── lib.rs
└── .gitignore
/CODEOWNERS:
--------------------------------------------------------------------------------
1 | * @davidrakosi
--------------------------------------------------------------------------------
/frontend/.npmrc:
--------------------------------------------------------------------------------
1 | package-lock=true
2 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "dependencies": {
3 | "react-hot-toast": "^2.3.0"
4 | }
5 | }
6 |
--------------------------------------------------------------------------------
/frontend/postcss.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | plugins: {
3 | tailwindcss: {},
4 | autoprefixer: {},
5 | },
6 | }
7 |
--------------------------------------------------------------------------------
/frontend/public/airbnb-logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CleverProgrammers/airbnb-clone-youtube/HEAD/frontend/public/airbnb-logo.png
--------------------------------------------------------------------------------
/frontend/constants/index.js:
--------------------------------------------------------------------------------
1 | import { PublicKey } from "@solana/web3.js";
2 |
3 | export const AIRBNB_PROGRAM_PUBKEY = new PublicKey("J5yzCZrNZJR6K5FUwwZ2SzjTvg8DzomcYqcaZJm27Y6v");
4 |
--------------------------------------------------------------------------------
/frontend/styles/globals.css:
--------------------------------------------------------------------------------
1 | @tailwind base;
2 | @tailwind components;
3 | @tailwind utilities;
4 |
5 | .phantom-button:hover {
6 | background-color: rgb(243 244 246) !important;
7 | }
8 |
--------------------------------------------------------------------------------
/frontend/utils/index.js:
--------------------------------------------------------------------------------
1 | export const authorFilter = (authorBase58PublicKey) => ({
2 | memcmp: {
3 | offset: 8, // Discriminator.
4 | bytes: authorBase58PublicKey,
5 | },
6 | })
7 |
--------------------------------------------------------------------------------
/frontend/utils/string.js:
--------------------------------------------------------------------------------
1 | export const truncate = (longString, limit = 10) => {
2 | if (longString.length > limit) {
3 | return longString.substring(0, limit) + '...'
4 | }
5 |
6 | return longString
7 | }
8 |
--------------------------------------------------------------------------------
/solana-solution/constant.rs:
--------------------------------------------------------------------------------
1 | use anchor_lang::prelude::*;
2 |
3 | #[constant]
4 | pub const USER_TAG: &[u8] = b"USER_STATE";
5 |
6 | #[constant]
7 | pub const AIRBNB_TAG: &[u8] = b"AIRBNB_STATE";
8 |
9 | #[constant]
10 | pub const BOOK_TAG: &[u8] = b"BOOK_STATE";
--------------------------------------------------------------------------------
/frontend/tailwind.config.js:
--------------------------------------------------------------------------------
1 | /** @type {import('tailwindcss').Config} */
2 | module.exports = {
3 | content: ['./pages/**/*.{js,ts,jsx,tsx}', './components/**/*.{js,ts,jsx,tsx}'],
4 | theme: {
5 | extend: {},
6 | },
7 | plugins: [require('tailwind-scrollbar-hide')],
8 | }
9 |
--------------------------------------------------------------------------------
/frontend/next.config.js:
--------------------------------------------------------------------------------
1 | /** @type {import('next').NextConfig} */
2 | const nextConfig = {
3 | reactStrictMode: true,
4 | env: {
5 | REACT_APP_CLUSTER: process.env.REACT_APP_CLUSTER,
6 | },
7 | images: {
8 | domains: [
9 | 'a0.muscache.com'
10 | ],
11 | },
12 | }
13 |
14 | module.exports = nextConfig
15 |
--------------------------------------------------------------------------------
/solana-solution/error.rs:
--------------------------------------------------------------------------------
1 | use anchor_lang::prelude::*;
2 |
3 | #[error_code]
4 | pub enum AirbnbError {
5 | #[msg("You are not authorized to perform this action.")]
6 | Unauthorized,
7 | #[msg("Not allowed")]
8 | NotAllowed,
9 | #[msg("Math operation overflow")]
10 | MathOverflow,
11 | #[msg("Already marked")]
12 | AlreadyMarked,
13 | }
--------------------------------------------------------------------------------
/frontend/pages/_app.js:
--------------------------------------------------------------------------------
1 | import dynamic from 'next/dynamic'
2 | import Head from 'next/head'
3 | import '../styles/globals.css'
4 |
5 | const WalletConnectionProvider = dynamic(() => import('../context/WalletConnectionProvider'), {
6 | ssr: false,
7 | })
8 |
9 | function MyApp({ Component, pageProps }) {
10 | return (
11 | <>
12 |
13 | Airbnb Clone
14 |
15 |
16 |
17 |
18 | >
19 | )
20 | }
21 |
22 | export default MyApp
23 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | node_modules
5 | .pnp
6 | .pnp.js
7 |
8 | # testing
9 | /coverage
10 |
11 | # next.js
12 | .next/
13 | out/
14 |
15 | # production
16 | /build
17 |
18 | # misc
19 | .DS_Store
20 | *.pem
21 | /.history
22 |
23 | # debug
24 | npm-debug.log*
25 | yarn-debug.log*
26 | yarn-error.log*
27 | .pnpm-debug.log*
28 |
29 | # local env files
30 | .env*.local
31 |
32 | # vercel
33 | .vercel
34 |
35 | # lock files
36 | yarn.lock
37 | package-lock.json
38 |
39 | # snyk code quality
40 | .dccache
41 |
42 | # anchor specific
43 | .anchor
44 | .DS_Store
45 | target
46 | **/*.rs.bk
47 |
--------------------------------------------------------------------------------
/frontend/context/WalletConnectionProvider.js:
--------------------------------------------------------------------------------
1 | import { ConnectionProvider, WalletProvider } from '@solana/wallet-adapter-react'
2 | import { WalletModalProvider } from '@solana/wallet-adapter-react-ui'
3 | import { PhantomWalletAdapter } from '@solana/wallet-adapter-wallets'
4 | import { useMemo } from 'react'
5 |
6 | const WalletConnectionProvider = ({ children }) => {
7 | const endpoint = useMemo(() => 'https://api.devnet.solana.com', [])
8 |
9 | const wallets = useMemo(() => [new PhantomWalletAdapter()], [])
10 |
11 | return (
12 |
13 |
14 | {children}
15 |
16 |
17 | )
18 | }
19 |
20 | export default WalletConnectionProvider
21 |
--------------------------------------------------------------------------------
/frontend/components/Listing/Listings.js:
--------------------------------------------------------------------------------
1 | import ListingItem from './ListingItem'
2 |
3 | function Listings({ connected, showReservedListing, listings, toggleEditListingModal, toggleReserveListingModal, removeListing, unreserveListing }) {
4 | return (
5 |
6 |
7 | {listings.map((listing) => (
8 | // console.log(listing.account)
9 |
10 | ))}
11 |
12 |
13 | )
14 | }
15 |
16 | export default Listings
17 |
--------------------------------------------------------------------------------
/solana-solution/states.rs:
--------------------------------------------------------------------------------
1 | use anchor_lang::prelude::*;
2 |
3 | #[account]
4 | #[derive(Default)]
5 | pub struct UserProfile {
6 | pub authority: Pubkey,
7 | pub last_airbnb: u8,
8 | pub airbnb_count: u8,
9 | }
10 |
11 | /// Size 2605 = 32 + 1 + 4 + 256 + 4 + 2048 + 4 + 256
12 | #[account]
13 | #[derive(Default)]
14 | pub struct AirbnbAccount {
15 | pub authority: Pubkey, // 32
16 | pub idx: u8, // 1
17 | pub location: String, // 4 + 256
18 | pub country: String, // 4 + 256
19 | pub image: String, // 4 + 2048
20 | pub price: String, // 4 + 256
21 | pub isReserved: bool // 8
22 | }
23 |
24 | #[account]
25 | #[derive(Default)]
26 | pub struct BookingAccount {
27 | pub authority: Pubkey, //32
28 | pub date: String, // 4 + 256
29 | pub idx: u8, // 1
30 | pub location: String, // 4 + 256
31 | pub country: String, // 4 + 256
32 | pub image: String, // 4 + 2048
33 | pub price: String, // 4 + 256
34 | pub isReserved: bool // 8
35 | }
--------------------------------------------------------------------------------
/frontend/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "frontend",
3 | "version": "0.1.0",
4 | "private": true,
5 | "scripts": {
6 | "dev": "next dev",
7 | "build": "next build",
8 | "start": "next start",
9 | "lint": "next lint",
10 | "export": "npm run build && next export -o _static"
11 | },
12 | "dependencies": {
13 | "@badrap/bar-of-progress": "^0.2.1",
14 | "@headlessui/react": "^1.6.6",
15 | "@heroicons/react": "^2.0.7",
16 | "@project-serum/anchor": "^0.25.0",
17 | "@solana/wallet-adapter-react": "^0.15.18",
18 | "@solana/wallet-adapter-react-ui": "^0.9.16",
19 | "@solana/wallet-adapter-wallets": "^0.18.6",
20 | "@solana/web3.js": "^1.56.0",
21 | "date-fns": "^2.29.2",
22 | "next": "12.1.5",
23 | "react": "^18.2.0",
24 | "react-date-range": "^1.4.0",
25 | "react-dom": "18.0.0",
26 | "react-hot-toast": "^2.3.0",
27 | "tailwind-scrollbar-hide": "^1.1.7"
28 | },
29 | "devDependencies": {
30 | "autoprefixer": "^10.4.8",
31 | "eslint": "8.13.0",
32 | "eslint-config-next": "12.1.5",
33 | "postcss": "^8.4.16",
34 | "tailwindcss": "^3.1.8"
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/frontend/components/Footer.js:
--------------------------------------------------------------------------------
1 | import { GlobeAmericasIcon } from '@heroicons/react/24/solid'
2 |
3 | function Footer() {
4 | return (
5 |
6 |
7 |
© 2022 Airbnb, Inc.
8 |
Privacy
9 |
Terms
10 |
Sitemap
11 |
Destinations
12 |
13 |
14 |
15 |
16 |
17 |
English (US)
18 |
19 |
23 |
24 |
Support & resources
25 |
26 |
27 |
28 | )
29 | }
30 |
31 | export default Footer
32 |
--------------------------------------------------------------------------------
/frontend/data/airbnb.js:
--------------------------------------------------------------------------------
1 | export default [
2 | {
3 | id: '1',
4 | name: 'Abiansemal',
5 | country: 'Indonesia',
6 | description: '',
7 | distance: {
8 | km: 1997,
9 | },
10 | price: 1608,
11 | rating: 4.87,
12 | imageURL: 'https://a0.muscache.com/im/pictures/e25a9b25-fa98-4160-bfd1-039287bf38b6.jpg',
13 | },
14 | {
15 | id: '2',
16 | name: 'Tambon Nong Kae',
17 | country: 'Thailand',
18 | description: '',
19 | distance: {
20 | km: 1050,
21 | },
22 | price: 494,
23 | rating: 4.95,
24 | imageURL: 'https://a0.muscache.com/im/pictures/77c3c61e-930a-4e7c-ab4d-59413c1f0b87.jpg',
25 | },
26 | {
27 | id: '3',
28 | name: 'Cabangan',
29 | country: 'Philippines',
30 | description: '',
31 | distance: {
32 | km: 2414,
33 | },
34 | price: 636,
35 | rating: 4.79,
36 | imageURL: 'https://a0.muscache.com/im/pictures/64fc0202-bcd7-46e3-87f1-3f7f0f8ead15.jpg',
37 | },
38 | {
39 | id: '4',
40 | name: 'Ko Samui District',
41 | country: 'Thailand',
42 | description: '',
43 | distance: {
44 | km: 718,
45 | },
46 | price: 478,
47 | rating: 4.77,
48 | imageURL: 'https://a0.muscache.com/im/pictures/36e5b4b5-0f4f-4703-a2c3-7a3cc9d9d82e.jpg',
49 | },
50 | ]
51 |
--------------------------------------------------------------------------------
/frontend/README.md:
--------------------------------------------------------------------------------
1 | This is a [Next.js](https://nextjs.org/) project bootstrapped with [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app).
2 |
3 | ## Getting Started
4 |
5 | First, run the development server:
6 |
7 | ```bash
8 | npm run dev
9 | # or
10 | yarn dev
11 | ```
12 |
13 | Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.
14 |
15 | You can start editing the page by modifying `pages/index.js`. The page auto-updates as you edit the file.
16 |
17 | [API routes](https://nextjs.org/docs/api-routes/introduction) can be accessed on [http://localhost:3000/api/hello](http://localhost:3000/api/hello). This endpoint can be edited in `pages/api/hello.js`.
18 |
19 | The `pages/api` directory is mapped to `/api/*`. Files in this directory are treated as [API routes](https://nextjs.org/docs/api-routes/introduction) instead of React pages.
20 |
21 | ## Learn More
22 |
23 | To learn more about Next.js, take a look at the following resources:
24 |
25 | - [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API.
26 | - [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial.
27 |
28 | You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js/) - your feedback and contributions are welcome!
29 |
30 | ## Deploy on Vercel
31 |
32 | The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js.
33 |
34 | Check out our [Next.js deployment documentation](https://nextjs.org/docs/deployment) for more details.
35 |
--------------------------------------------------------------------------------
/frontend/components/Listing/ListingItem.js:
--------------------------------------------------------------------------------
1 | import { StarIcon } from '@heroicons/react/20/solid'
2 | import { PencilIcon, TrashIcon, HeartIcon } from '@heroicons/react/24/outline'
3 |
4 | function ListingItem({ idx, publickey, connected, showReservedListing,location,country, date, distance, price, rating, image, isReserved, reservation, removeListing, toggleEditListingModal, toggleReserveListingModal, unreserveListing }) {
5 |
6 | const formatNumber = (num) => {
7 | return num.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ',')
8 | }
9 |
10 | const handleReserve = () => {
11 | if (isReserved) {
12 | unreserveListing(publickey,idx)
13 | return
14 | }
15 |
16 | toggleReserveListingModal(true, idx)
17 | }
18 | return (
19 |
20 |
21 |

22 |
23 | {connected && (
24 |
25 |
toggleEditListingModal(idx)} className="w-6 h-6 text-white opacity-80" />
26 | removeListing(publickey,idx)} className="w-6 h-6 text-white opacity-80" />
27 |
28 |
29 | )}
30 |
31 |
32 |
33 |
34 |
35 | {location}, {country}
36 |
37 |
38 |
42 |
43 |
44 |
{788} kilometers
45 |
46 | {showReservedListing &&
{date}
}
47 |
48 |
49 | RM {price}
50 | night
51 |
52 |
53 |
54 | )
55 | }
56 |
57 | export default ListingItem
58 |
--------------------------------------------------------------------------------
/frontend/constants/airbnb.json:
--------------------------------------------------------------------------------
1 | {"version":"0.1.0","name":"clever_airbnb","constants":[{"name":"USER_TAG","type":{"defined":"&[u8]"},"value":"b\"USER_STATE\""},{"name":"AIRBNB_TAG","type":{"defined":"&[u8]"},"value":"b\"AIRBNB_STATE\""},{"name":"BOOK_TAG","type":{"defined":"&[u8]"},"value":"b\"BOOK_STATE\""}],"instructions":[{"name":"initializeUser","accounts":[{"name":"authority","isMut":true,"isSigner":true},{"name":"userProfile","isMut":true,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[]},{"name":"addAirbnb","accounts":[{"name":"userProfile","isMut":true,"isSigner":false},{"name":"airbnbAccount","isMut":true,"isSigner":false},{"name":"authority","isMut":true,"isSigner":true},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[{"name":"location","type":"string"},{"name":"country","type":"string"},{"name":"price","type":"string"},{"name":"img","type":"string"}]},{"name":"updateAirbnb","accounts":[{"name":"userProfile","isMut":true,"isSigner":false},{"name":"airbnbAccount","isMut":true,"isSigner":false},{"name":"authority","isMut":true,"isSigner":true},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[{"name":"airbnbIdx","type":"u8"},{"name":"location","type":"string"},{"name":"country","type":"string"},{"name":"price","type":"string"},{"name":"img","type":"string"}]},{"name":"removeAirbnb","accounts":[{"name":"userProfile","isMut":true,"isSigner":false},{"name":"airbnbAccount","isMut":true,"isSigner":false},{"name":"authority","isMut":true,"isSigner":true},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[{"name":"airbnbIdx","type":"u8"}]},{"name":"bookAirbnb","accounts":[{"name":"userProfile","isMut":true,"isSigner":false},{"name":"bookingAccount","isMut":true,"isSigner":false},{"name":"authority","isMut":true,"isSigner":true},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[{"name":"idx","type":"u8"},{"name":"date","type":"string"},{"name":"location","type":"string"},{"name":"country","type":"string"},{"name":"price","type":"string"},{"name":"img","type":"string"}]},{"name":"cancelBooking","accounts":[{"name":"userProfile","isMut":true,"isSigner":false},{"name":"bookingAccount","isMut":true,"isSigner":false},{"name":"authority","isMut":true,"isSigner":true},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[{"name":"bookingIdx","type":"u8"}]}],"accounts":[{"name":"UserProfile","type":{"kind":"struct","fields":[{"name":"authority","type":"publicKey"},{"name":"lastAirbnb","type":"u8"},{"name":"airbnbCount","type":"u8"}]}},{"name":"AirbnbAccount","type":{"kind":"struct","fields":[{"name":"authority","type":"publicKey"},{"name":"idx","type":"u8"},{"name":"location","type":"string"},{"name":"country","type":"string"},{"name":"image","type":"string"},{"name":"price","type":"string"},{"name":"isReserved","type":"bool"}]}},{"name":"BookingAccount","type":{"kind":"struct","fields":[{"name":"authority","type":"publicKey"},{"name":"date","type":"string"},{"name":"idx","type":"u8"},{"name":"location","type":"string"},{"name":"country","type":"string"},{"name":"image","type":"string"},{"name":"price","type":"string"},{"name":"isReserved","type":"bool"}]}}]}
--------------------------------------------------------------------------------
/frontend/components/FilterMenu.js:
--------------------------------------------------------------------------------
1 | import Image from 'next/image'
2 | import { AdjustmentsHorizontalIcon } from '@heroicons/react/24/outline'
3 |
4 | function FilterMenu() {
5 | const menus = [
6 | {
7 | title: 'OMG!',
8 | icon: 'https://a0.muscache.com/pictures/c5a4f6fc-c92c-4ae8-87dd-57f1ff1b89a6.jpg',
9 | },
10 | {
11 | title: 'Beach',
12 | icon: 'https://a0.muscache.com/pictures/10ce1091-c854-40f3-a2fb-defc2995bcaf.jpg',
13 | },
14 | {
15 | title: 'Amazing pools',
16 | icon: 'https://a0.muscache.com/pictures/3fb523a0-b622-4368-8142-b5e03df7549b.jpg',
17 | },
18 | {
19 | title: 'Tiny homes',
20 | icon: 'https://a0.muscache.com/pictures/35919456-df89-4024-ad50-5fcb7a472df9.jpg',
21 | },
22 | {
23 | title: 'Islands',
24 | icon: 'https://a0.muscache.com/pictures/8e507f16-4943-4be9-b707-59bd38d56309.jpg',
25 | },
26 | {
27 | title: 'National parks',
28 | icon: 'https://a0.muscache.com/pictures/c0a24c04-ce1f-490c-833f-987613930eca.jpg',
29 | },
30 | {
31 | title: 'Design',
32 | icon: 'https://a0.muscache.com/pictures/50861fca-582c-4bcc-89d3-857fb7ca6528.jpg',
33 | },
34 | {
35 | title: 'Arctic',
36 | icon: 'https://a0.muscache.com/pictures/8b44f770-7156-4c7b-b4d3-d92549c8652f.jpg',
37 | },
38 | {
39 | title: 'Amazing views',
40 | icon: 'https://a0.muscache.com/pictures/3b1eb541-46d9-4bef-abc4-c37d77e3c21b.jpg',
41 | },
42 | {
43 | title: 'Lakefront',
44 | icon: 'https://a0.muscache.com/pictures/677a041d-7264-4c45-bb72-52bff21eb6e8.jpg',
45 | },
46 | {
47 | title: 'Surfing',
48 | icon: 'https://a0.muscache.com/pictures/957f8022-dfd7-426c-99fd-77ed792f6d7a.jpg',
49 | },
50 | {
51 | title: 'Cabins',
52 | icon: 'https://a0.muscache.com/pictures/732edad8-3ae0-49a8-a451-29a8010dcc0c.jpg',
53 | },
54 | ]
55 |
56 | return (
57 |
58 |
59 | {menus.map((menu, index) => (
60 |
61 |
62 |
63 |
64 |
65 |
{menu.title}
66 |
67 | ))}
68 |
69 |
73 |
74 | )
75 | }
76 |
77 | export default FilterMenu
78 |
--------------------------------------------------------------------------------
/frontend/data/listings.js:
--------------------------------------------------------------------------------
1 | export default [
2 | {
3 | id: '1',
4 | location: {
5 | name: 'Abiansemal',
6 | country: 'Indonesia',
7 | },
8 | description: '',
9 | distance: {
10 | km: 1997,
11 | },
12 | availability: {
13 | startDate: {
14 | month: 7,
15 | day: 9,
16 | },
17 | endDate: {
18 | month: 7,
19 | day: 14,
20 | },
21 | },
22 | price: {
23 | perNight: 1608,
24 | },
25 | rating: 4.87,
26 | imageURL: 'https://a0.muscache.com/im/pictures/e25a9b25-fa98-4160-bfd1-039287bf38b6.jpg',
27 | isReserved: false,
28 | reservation: null,
29 | },
30 | {
31 | id: '2',
32 | location: {
33 | name: 'Tambon Nong Kae',
34 | country: 'Thailand',
35 | },
36 | description: '',
37 | distance: {
38 | km: 1050,
39 | },
40 | availability: {
41 | startDate: {
42 | month: 9,
43 | day: 8,
44 | },
45 | endDate: {
46 | month: 9,
47 | day: 13,
48 | },
49 | },
50 | price: {
51 | perNight: 494,
52 | },
53 | rating: 4.95,
54 | imageURL: 'https://a0.muscache.com/im/pictures/77c3c61e-930a-4e7c-ab4d-59413c1f0b87.jpg',
55 | isReserved: false,
56 | reservation: null,
57 | },
58 | {
59 | id: '3',
60 | location: {
61 | name: 'Cabangan',
62 | country: 'Philippines',
63 | },
64 | description: '',
65 | distance: {
66 | km: 2414,
67 | },
68 | availability: {
69 | startDate: {
70 | month: 9,
71 | day: 4,
72 | },
73 | endDate: {
74 | month: 9,
75 | day: 10,
76 | },
77 | },
78 | price: {
79 | perNight: 636,
80 | },
81 | rating: 4.79,
82 | imageURL: 'https://a0.muscache.com/im/pictures/64fc0202-bcd7-46e3-87f1-3f7f0f8ead15.jpg',
83 | isReserved: false,
84 | reservation: null,
85 | },
86 | {
87 | id: '4',
88 | location: {
89 | name: 'Ko Samui District',
90 | country: 'Thailand',
91 | },
92 | description: '',
93 | distance: {
94 | km: 718,
95 | },
96 | availability: {
97 | startDate: {
98 | month: 9,
99 | day: 26,
100 | },
101 | endDate: {
102 | month: 10,
103 | day: 3,
104 | },
105 | },
106 | price: {
107 | perNight: 478,
108 | },
109 | rating: 4.77,
110 | imageURL: 'https://a0.muscache.com/im/pictures/36e5b4b5-0f4f-4703-a2c3-7a3cc9d9d82e.jpg',
111 | isReserved: false,
112 | reservation: null,
113 | },
114 | ]
115 |
--------------------------------------------------------------------------------
/frontend/components/Listing/ReserveListingModal.js:
--------------------------------------------------------------------------------
1 | import { Dialog, Transition } from '@headlessui/react'
2 | import { Fragment, useState } from 'react'
3 | import { DateRangePicker } from 'react-date-range'
4 | import 'react-date-range/dist/styles.css' // main style file
5 | import 'react-date-range/dist/theme/default.css' // theme css file
6 | import { format } from 'date-fns'
7 |
8 | export default function ReserveListingModal({ reserveListing,currentEditListing, reserveListingModalOpen, setReserveListingModalOpen }) {
9 | const [startDate, setStartDate] = useState(new Date())
10 | const [endDate, setEndDate] = useState(new Date())
11 |
12 | const selectionRange = {
13 | startDate,
14 | endDate,
15 | key: 'selection',
16 | }
17 |
18 | const handleSelect = (ranges) => {
19 | setStartDate(ranges.selection.startDate)
20 | setEndDate(ranges.selection.endDate)
21 | }
22 |
23 | const closeModal = () => {
24 | setReserveListingModalOpen(false)
25 | }
26 |
27 | const onConfirm = (e) => {
28 | e.preventDefault()
29 |
30 | const formattedStartDate = format(new Date(startDate), 'MMM d')
31 | const formattedEndDate = format(new Date(endDate), 'MMM d')
32 | const range = `${formattedStartDate} - ${formattedEndDate}`
33 |
34 | reserveListing(currentEditListing.account,range)
35 |
36 | closeModal()
37 | }
38 |
39 | return (
40 |
41 |
68 |
69 | )
70 | }
71 |
--------------------------------------------------------------------------------
/frontend/pages/index.js:
--------------------------------------------------------------------------------
1 | import Head from 'next/head'
2 | import Header from '../components/Header'
3 | import Footer from '../components/Footer'
4 | import FilterMenu from '../components/FilterMenu'
5 | import Listings from '../components/Listing/Listings'
6 | import { useMemo, useState,useEffect } from 'react'
7 | import listingsData from '../data/airbnb'
8 | import AddListingModal from '../components/Listing/AddListingModal'
9 | import EditListingModal from '../components/Listing/EditListingModal'
10 | import { useWallet } from '@solana/wallet-adapter-react'
11 | import ReserveListingModal from '../components/Listing/ReserveListingModal'
12 | import { useAirbnb } from '../hooks/useAirbnb'
13 |
14 |
15 | import { PublicKey } from "@solana/web3.js";
16 |
17 |
18 |
19 | export default function Home() {
20 |
21 |
22 | const {initializeUser, airbnbs, bookings, transactionPending, addAirbnb, updateAirbnb, removeAirbnb, bookAirbnb, cancelBooking, initialized} = useAirbnb()
23 | // console.log(airbnbs, "👈")
24 |
25 |
26 | const { connected, publicKey } = useWallet()
27 | const [showReservedListing, setShowReservedListing] = useState(false)
28 | const [listings, setListings] = useState(listingsData)
29 | const [addListingModalOpen, setAddListingModalOpen] = useState(false)
30 | const [editListingModalOpen, setEditListingModalOpen] = useState(false)
31 | const [reserveListingModalOpen, setReserveListingModalOpen] = useState(false)
32 | const [currentEditListingID, setCurrentEditListingID] = useState(null)
33 | const [currentReserveListingID, setCurrentReserveListingID] = useState(null)
34 | const currentEditListing = useMemo(() => airbnbs.find((listing) => listing.account.idx === currentEditListingID), [currentEditListingID])
35 | const displayListings = useMemo(() => (showReservedListing ? bookings : airbnbs), [showReservedListing, airbnbs])
36 |
37 | const toggleShowReservedListing = () => {
38 | setShowReservedListing(!showReservedListing)
39 | }
40 |
41 |
42 |
43 | const toggleEditListingModal = (listingID) => {
44 | setCurrentEditListingID(listingID)
45 |
46 | setEditListingModalOpen(true)
47 | }
48 |
49 |
50 | const removeListing = (listingID) => {
51 | setListings(listings.filter((listing) => listing.id !== listingID))
52 | }
53 |
54 | const toggleReserveListingModal = (value, listingID) => {
55 | setCurrentEditListingID(listingID)
56 |
57 | setReserveListingModalOpen(value)
58 | }
59 |
60 | const reserveListing = ({location, country, price, image},range) => {
61 | console.log(location, country, price, image, "BETTT",range)
62 | }
63 |
64 | const unreserveListing = () => {
65 |
66 | }
67 |
68 | return (
69 |
70 |
71 |
Airbnb Clone
72 |
73 |
74 |
98 | )
99 | }
100 |
--------------------------------------------------------------------------------
/frontend/components/Listing/AddListingModal.js:
--------------------------------------------------------------------------------
1 | import { Dialog, Transition } from '@headlessui/react'
2 | import { Fragment, useState } from 'react'
3 |
4 | export default function AddListingModal({ addAirbnb, addListingModalOpen, setAddListingModalOpen }) {
5 | const [location, setLocation] = useState('')
6 | const [country, setCountry] = useState('')
7 | const [price, setPrice] = useState(0)
8 | const [imageURL, setImageURL] = useState('')
9 |
10 | const closeModal = () => {
11 | setAddListingModalOpen(false)
12 | }
13 |
14 | const onCreate = (e) => {
15 | e.preventDefault()
16 |
17 | addAirbnb({
18 | location,
19 | country,
20 | price,
21 | imageURL,
22 | })
23 |
24 | closeModal()
25 | }
26 |
27 | return (
28 |
29 |
76 |
77 | )
78 | }
79 |
--------------------------------------------------------------------------------
/frontend/components/Listing/EditListingModal.js:
--------------------------------------------------------------------------------
1 | import { Dialog, Transition } from '@headlessui/react'
2 | import { Fragment, useState } from 'react'
3 |
4 | export default function EditListingModal({ editListing, currentEditListing, editListingModalOpen, setEditListingModalOpen }) {
5 | const [location, setLocation] = useState('')
6 | const [country, setCountry] = useState('')
7 | const [price, setPrice] = useState(0)
8 | const [imageURL, setImageURL] = useState('')
9 |
10 | const closeModal = () => {
11 | setEditListingModalOpen(false)
12 | }
13 |
14 | const onEdit = (e) => {
15 | e.preventDefault()
16 |
17 | editListing({
18 | airbnbPda: currentEditListing.publicKey,
19 | airbnbIdx: currentEditListing?.account.idx,
20 | location,
21 | country,
22 | price,
23 | imageURL,
24 | })
25 |
26 | closeModal()
27 | }
28 |
29 | return (
30 |
31 |
78 |
79 | )
80 | }
81 |
--------------------------------------------------------------------------------
/frontend/components/Header.js:
--------------------------------------------------------------------------------
1 | import { GlobeAmericasIcon, Bars3Icon, UserCircleIcon, MagnifyingGlassIcon } from '@heroicons/react/24/solid'
2 | import { WalletMultiButton } from '@solana/wallet-adapter-react-ui'
3 | import Image from 'next/image'
4 | import { truncate } from '../utils/string'
5 | require('@solana/wallet-adapter-react-ui/styles.css')
6 |
7 | function Header({ connected, publicKey, initializeUser , initialized, transactionPending}) {
8 | return (
9 |
58 | )
59 | }
60 |
61 | export default Header
62 |
--------------------------------------------------------------------------------
/solana-solution/lib.rs:
--------------------------------------------------------------------------------
1 | use anchor_lang::prelude::*;
2 | pub mod constant;
3 | pub mod states;
4 | use crate::{constant::*, states::*};
5 |
6 | declare_id!("J5yzCZrNZJR6K5FUwwZ2SzjTvg8DzomcYqcaZJm27Y6v");
7 |
8 | #[program]
9 | pub mod clever_airbnb {
10 | use super::*;
11 |
12 | pub fn initialize_user(
13 | ctx: Context
14 | ) -> Result<()> {
15 | // Initialize user profile with default data
16 |
17 | let user_profile = &mut ctx.accounts.user_profile;
18 | user_profile.authority = ctx.accounts.authority.key();
19 | user_profile.last_airbnb = 0;
20 | user_profile.airbnb_count = 0;
21 |
22 | Ok(())
23 | }
24 |
25 | pub fn add_airbnb(
26 | ctx: Context,
27 | location: String,
28 | country: String,
29 | price: String,
30 | img: String,
31 | ) -> Result<()> {
32 | let airbnb_account = &mut ctx.accounts.airbnb_account;
33 | let user_profile = &mut ctx.accounts.user_profile;
34 |
35 | // Fill contents with argument
36 | airbnb_account.authority = ctx.accounts.authority.key();
37 | airbnb_account.idx = user_profile.last_airbnb;
38 | airbnb_account.location = location;
39 | airbnb_account.country = country;
40 | airbnb_account.price = price;
41 | airbnb_account.image = img;
42 | airbnb_account.isReserved = false;
43 |
44 | // Increase airbnb idx for PDA
45 | user_profile.last_airbnb = user_profile.last_airbnb
46 | .checked_add(1)
47 | .unwrap();
48 |
49 | // Increase total airbnb count
50 | user_profile.airbnb_count = user_profile.airbnb_count
51 | .checked_add(1)
52 | .unwrap();
53 |
54 | Ok(())
55 | }
56 |
57 | pub fn update_airbnb(
58 | ctx: Context,
59 | airbnb_idx: u8,
60 | location: String,
61 | country: String,
62 | price: String,
63 | img: String,
64 | ) -> Result<()> {
65 | let airbnb_account = &mut ctx.accounts.airbnb_account;
66 |
67 | // Mark todo
68 | airbnb_account.location = location;
69 | airbnb_account.country = country;
70 | airbnb_account.price = price;
71 | airbnb_account.image = img;
72 | Ok(())
73 | }
74 |
75 | pub fn remove_airbnb(ctx: Context, _airbnb_idx: u8) -> Result<()> {
76 | // Decreate total airbnb count
77 | let user_profile = &mut ctx.accounts.user_profile;
78 | user_profile.airbnb_count = user_profile.airbnb_count
79 | .checked_sub(1)
80 | .unwrap();
81 |
82 | // No need to decrease last airbnb idx
83 |
84 | // Todo PDA already closed in context
85 |
86 | Ok(())
87 | }
88 |
89 | // Need a function that reserves an Airbnb
90 | pub fn book_airbnb(
91 | ctx: Context,
92 | idx: u8,
93 | date: String,
94 | location: String,
95 | country: String,
96 | price: String,
97 | img: String,
98 | ) -> Result<()> {
99 | let booking_account = &mut ctx.accounts.booking_account;
100 |
101 | // // Fill contents with argument
102 | booking_account.authority = ctx.accounts.authority.key();
103 | booking_account.idx = idx;
104 | booking_account.date = date;
105 | booking_account.location = location;
106 | booking_account.country = country;
107 | booking_account.price = price;
108 | booking_account.image = img;
109 | booking_account.isReserved = true;
110 |
111 |
112 | Ok(())
113 | }
114 |
115 | pub fn cancel_booking(ctx: Context, _booking_idx: u8) -> Result<()> {
116 | // Decreate total airbnb count
117 | let user_profile = &mut ctx.accounts.user_profile;
118 |
119 | Ok(())
120 | }
121 | }
122 |
123 | #[derive(Accounts)]
124 | pub struct InitializeUser<'info> {
125 | #[account(mut)]
126 | pub authority: Signer<'info>,
127 |
128 | #[account(
129 | init,
130 | seeds = [USER_TAG, authority.key().as_ref()],
131 | bump,
132 | payer = authority,
133 | space = 8 + std::mem::size_of::(),
134 | )]
135 | pub user_profile: Box>,
136 |
137 | pub system_program: Program<'info, System>,
138 | }
139 |
140 | #[derive(Accounts)]
141 | #[instruction()]
142 | pub struct AddAirbnb<'info> {
143 | #[account(
144 | mut,
145 | seeds = [USER_TAG, authority.key().as_ref()],
146 | bump,
147 | has_one = authority,
148 | )]
149 | pub user_profile: Box>,
150 |
151 | #[account(
152 | init,
153 | seeds = [AIRBNB_TAG, authority.key().as_ref(), &[user_profile.last_airbnb]],
154 | bump,
155 | payer = authority,
156 | space = 2865 + 8,
157 | )]
158 | pub airbnb_account: Box>,
159 |
160 | #[account(mut)]
161 | pub authority: Signer<'info>,
162 |
163 | pub system_program: Program<'info, System>,
164 | }
165 |
166 | #[derive(Accounts)]
167 | #[instruction(airbnb_idx: u8)]
168 | pub struct UpdateAirbnb<'info> {
169 | #[account(
170 | mut,
171 | seeds = [USER_TAG, authority.key().as_ref()],
172 | bump,
173 | has_one = authority,
174 | )]
175 | pub user_profile: Box>,
176 |
177 | #[account(
178 | mut,
179 | seeds = [AIRBNB_TAG, authority.key().as_ref(), &[airbnb_idx].as_ref()],
180 | bump,
181 | has_one = authority,
182 | )]
183 | pub airbnb_account: Box>,
184 |
185 | #[account(mut)]
186 | pub authority: Signer<'info>,
187 |
188 | pub system_program: Program<'info, System>,
189 | }
190 |
191 | #[derive(Accounts)]
192 | #[instruction(airbnb_idx: u8)]
193 | pub struct RemoveAirbnb<'info> {
194 | #[account(
195 | mut,
196 | seeds = [USER_TAG, authority.key().as_ref()],
197 | bump,
198 | has_one = authority,
199 | )]
200 | pub user_profile: Box>,
201 |
202 | #[account(
203 | mut,
204 | close = authority,
205 | seeds = [AIRBNB_TAG, authority.key().as_ref(), &[airbnb_idx].as_ref()],
206 | bump,
207 | has_one = authority,
208 | )]
209 | pub airbnb_account: Box>,
210 |
211 | #[account(mut)]
212 | pub authority: Signer<'info>,
213 |
214 | pub system_program: Program<'info, System>,
215 | }
216 |
217 | // #[derive(Accounts)]
218 | // #[instruction()]
219 | // pub struct BookAirbnb<'info> {
220 | // #[account(
221 | // mut,
222 | // seeds = [USER_TAG, authority.key().as_ref()],
223 | // bump,
224 | // has_one = authority,
225 | // )]
226 | // pub user_profile: Box>,
227 |
228 | // #[account(
229 | // init,
230 | // seeds = [BOOK_TAG, airbnb_account.key().as_ref()],
231 | // bump,
232 | // payer = booking_authority,
233 | // space = 3125 + 8,
234 | // )]
235 | // pub booking_account: Box>,
236 |
237 | // #[account(mut)]
238 | // pub authority: Signer<'info>,
239 |
240 | // pub system_program: Program<'info, System>,
241 | // }
242 |
243 | #[derive(Accounts)]
244 | #[instruction()]
245 | pub struct BookAirbnb<'info> {
246 | #[account(
247 | mut,
248 | seeds = [USER_TAG, authority.key().as_ref()],
249 | bump,
250 | has_one = authority,
251 | )]
252 | pub user_profile: Box>,
253 |
254 | #[account(
255 | init,
256 | seeds = [BOOK_TAG, authority.key().as_ref() ],
257 | bump,
258 | payer = authority,
259 | space = 3125 + 8,
260 | )]
261 | pub booking_account: Box>,
262 |
263 | #[account(mut)]
264 | pub authority: Signer<'info>,
265 |
266 | pub system_program: Program<'info, System>,
267 | }
268 |
269 | #[derive(Accounts)]
270 | pub struct CancelBook<'info> {
271 | #[account(
272 | mut,
273 | seeds = [USER_TAG, authority.key().as_ref()],
274 | bump,
275 | has_one = authority,
276 | )]
277 | pub user_profile: Box>,
278 |
279 | #[account(
280 | mut,
281 | close = authority,
282 | seeds = [BOOK_TAG, authority.key().as_ref()],
283 | bump,
284 | has_one = authority,
285 | )]
286 | pub booking_account: Box>,
287 |
288 | #[account(mut)]
289 | pub authority: Signer<'info>,
290 |
291 | pub system_program: Program<'info, System>,
292 | }
293 |
--------------------------------------------------------------------------------
/frontend/hooks/useAirbnb.js:
--------------------------------------------------------------------------------
1 | import * as anchor from '@project-serum/anchor'
2 | import { useEffect, useMemo, useState } from 'react'
3 | import toast from 'react-hot-toast'
4 | import { AIRBNB_PROGRAM_PUBKEY } from '../constants'
5 | import airbnbIDL from '../constants/airbnb.json'
6 | import { SystemProgram } from '@solana/web3.js'
7 | import { utf8 } from '@project-serum/anchor/dist/cjs/utils/bytes'
8 | import { findProgramAddressSync } from '@project-serum/anchor/dist/cjs/utils/pubkey'
9 | import { useAnchorWallet, useConnection, useWallet } from '@solana/wallet-adapter-react'
10 | import { authorFilter } from '../utils'
11 | import { PublicKey } from '@solana/web3.js'
12 | import { set } from 'date-fns'
13 | import { tr } from 'date-fns/locale'
14 |
15 |
16 | // Static data that reflects the todo struct of the solana program
17 |
18 |
19 | export function useAirbnb() {
20 | const { connection } = useConnection()
21 | const { publicKey } = useWallet()
22 | const anchorWallet = useAnchorWallet()
23 |
24 | const [initialized, setInitialized] = useState(false)
25 | const [user,setUser] = useState({})
26 | const [airbnbs, setAirbnbs] = useState([])
27 | const [bookings, setBookings] = useState([])
28 | const [lastAirbnb, setLastAirbnb] = useState(0)
29 | const [lastBookId,setLastBookId] = useState(0)
30 | const [loading, setLoading] = useState(false)
31 | const [transactionPending, setTransactionPending] = useState(false)
32 |
33 | // const program = new PublicKey(
34 | // "8ktkADKMec8BE1q47k6LnMWqYpNTjqtinZ6ng219wMwf"
35 | // );
36 |
37 | const program = useMemo(() => {
38 | if (anchorWallet) {
39 | const provider = new anchor.AnchorProvider(connection, anchorWallet, anchor.AnchorProvider.defaultOptions())
40 | return new anchor.Program(airbnbIDL, AIRBNB_PROGRAM_PUBKEY, provider)
41 | }
42 | }, [connection, anchorWallet])
43 |
44 |
45 | useEffect(()=> {
46 |
47 | const start = async () => {
48 | if (program && publicKey && !transactionPending) {
49 | try {
50 | const [profilePda, profileBump] = await findProgramAddressSync([utf8.encode('USER_STATE'), publicKey.toBuffer()], program.programId)
51 | const profileAccount = await program.account.userProfile.fetch(profilePda)
52 | console.log(profileAccount)
53 | if (profileAccount) {
54 | setLastAirbnb(profileAccount.lastAirbnb)
55 | setInitialized(true)
56 | setLoading(true)
57 |
58 |
59 | const listings = await program.account.airbnbAccount.all();
60 | const allBookings = await program.account.bookingAccount.all();
61 | setUser(profileAccount.toString())
62 | setAirbnbs(listings)
63 |
64 | const myBookings = allBookings.filter(booking=> booking.account.authority.toString() == profileAccount.authority.toString())
65 |
66 | setBookings(myBookings)
67 | } else {
68 | setInitialized(false)
69 | }
70 | } catch (error) {
71 | console.log(error)
72 | setInitialized(false)
73 | } finally {
74 | setLoading(false)
75 | }
76 | }
77 |
78 | }
79 |
80 | start()
81 |
82 | },[publicKey,program, transactionPending])
83 |
84 | const initializeUser = async () => {
85 | if (program && publicKey) {
86 | try {
87 | setTransactionPending(true)
88 | setLoading(true)
89 | const [profilePda] = findProgramAddressSync([utf8.encode('USER_STATE'), publicKey.toBuffer()], program.programId)
90 |
91 | const tx = await program.methods
92 | .initializeUser()
93 | .accounts({
94 | userProfile: profilePda,
95 | authority: publicKey,
96 | systemProgram: SystemProgram.programId,
97 | })
98 | .rpc()
99 | setInitialized(true)
100 | toast.success('Successfully initialized user.')
101 | } catch (error) {
102 | console.log(error)
103 | } finally {
104 | setLoading(false)
105 | setTransactionPending(false)
106 | }
107 | }
108 | }
109 |
110 | const addAirbnb = async ({location, country, price, imageURL}) => {
111 | console.log(location,country,imageURL,price, "YOOO" )
112 | if (program && publicKey) {
113 | setTransactionPending(true)
114 | setLoading(true)
115 | try {
116 | const [profilePda] = findProgramAddressSync([utf8.encode('USER_STATE'), publicKey.toBuffer()], program.programId)
117 | const [airbnbPda] = findProgramAddressSync([utf8.encode('AIRBNB_STATE'), publicKey.toBuffer(), Uint8Array.from([lastAirbnb])], program.programId)
118 |
119 | console.log(publicKey.toString(), program.programId, profilePda.toString(), airbnbPda.toString(), lastAirbnb)
120 |
121 | await program.methods
122 | .addAirbnb(location, country, price, imageURL)
123 | .accounts({
124 | userProfile: profilePda,
125 | airbnbAccount: airbnbPda,
126 | authority: publicKey,
127 | systemProgram: SystemProgram.programId,
128 | })
129 | .rpc()
130 | toast.success("SUCCESSFULLY ADDED A LISTING")
131 | } catch (error) {
132 | console.error(error)
133 | } finally {
134 | setTransactionPending(false)
135 | setLoading(false)
136 | }
137 | }
138 | }
139 |
140 | const updateAirbnb = async ({airbnbPda, airbnbIdx,location, country, price, imageURL}) => {
141 | console.log(airbnbPda.toString())
142 | if (program && publicKey) {
143 | try {
144 | setLoading(true)
145 | setTransactionPending(true)
146 | const [profilePda] = findProgramAddressSync([utf8.encode('USER_STATE'), publicKey.toBuffer()], program.programId)
147 |
148 | await program.methods
149 | .updateAirbnb(airbnbIdx,location, country, price, imageURL)
150 | .accounts({
151 | userProfile: profilePda,
152 | airbnbAccount: airbnbPda,
153 | authority: publicKey,
154 | systemProgram: SystemProgram.programId,
155 | })
156 | .rpc()
157 | toast.success('Successfully EDIT AIRBNB.')
158 | } catch (error) {
159 | console.error(error)
160 | } finally {
161 | setLoading(false)
162 | setTransactionPending(false)
163 | }
164 | }
165 | }
166 |
167 | const editListing = ({ publicKey, idx, location, country, price, description, imageURL }) => {
168 | console.log(publicKey,idx, location, country, price, description, imageURL, "YAY" )
169 | }
170 |
171 | const removeAirbnb = async (airbnbPda, airbnbIdx) => {
172 | if (program && publicKey) {
173 | try {
174 | setTransactionPending(true)
175 | setLoading(true)
176 | const [profilePda, profileBump] = findProgramAddressSync([utf8.encode('USER_STATE'), publicKey.toBuffer()], program.programId)
177 | console.log(airbnbPda.toString(), airbnbIdx, publicKey.toString(), profilePda.toString())
178 | await program.methods
179 | .removeAirbnb(airbnbIdx)
180 | .accounts({
181 | userProfile: profilePda,
182 | airbnbAccount: airbnbPda,
183 | authority: publicKey,
184 | systemProgram: SystemProgram.programId,
185 | })
186 | .rpc()
187 | toast.success("Deleted listing")
188 | } catch (error) {
189 | console.log(error)
190 | } finally {
191 | setLoading(false)
192 | setTransactionPending(false)
193 | }
194 | }
195 | }
196 |
197 | const bookAirbnb = async ({location, country, price, image},date) => {
198 | console.log(location, country, price, image, "BETTT")
199 |
200 | const id = lastBookId + 1
201 | if (program && publicKey) {
202 | try {
203 | setLoading(true)
204 | setTransactionPending(true)
205 | const [profilePda] = findProgramAddressSync([utf8.encode('USER_STATE'), publicKey.toBuffer()], program.programId)
206 | const [bookPda] = findProgramAddressSync([utf8.encode('BOOK_STATE'), publicKey.toBuffer()], program.programId)
207 | console.log(profilePda)
208 | await program.methods
209 | .bookAirbnb(id,date,location, country, price, image)
210 | .accounts({
211 | userProfile: profilePda,
212 | bookingAccount: bookPda,
213 | authority: publicKey,
214 | systemProgram: SystemProgram.programId,
215 | })
216 | .rpc()
217 | toast.success("SUCCESSFULLY BOOOOOKED")
218 | setLastBookId(id)
219 | } catch (error) {
220 | console.error(error)
221 | } finally {
222 | setLoading(false)
223 | setTransactionPending(false)
224 | }
225 | }
226 | }
227 |
228 | const cancelBooking = async (bookingPda,idx) => {
229 | console.log("RUNNING")
230 | if (program && publicKey) {
231 | try {
232 | setLoading(true)
233 | setTransactionPending(true)
234 | const [profilePda] = findProgramAddressSync([utf8.encode('USER_STATE'), publicKey.toBuffer()], program.programId)
235 | await program.methods
236 | .cancelBooking(idx)
237 | .accounts({
238 | userProfile: profilePda,
239 | bookingAccount: bookingPda,
240 | authority: publicKey,
241 | systemProgram: SystemProgram.programId,
242 | })
243 | .rpc()
244 | toast.success("Canceled Booking")
245 | } catch (error) {
246 | console.log(error)
247 | } finally {
248 | setLoading(false)
249 | setTransactionPending(false)
250 | }
251 | }
252 | }
253 |
254 | // const removeListing = (listingID) => {
255 | // setListings(listings.filter((listing) => listing.id !== listingID))
256 | // }
257 |
258 | // const addListing = ({ location, country, price, description, imageURL }) => {
259 | // const id = listings.length + 1
260 |
261 | // setListings([
262 | // ...listings,
263 | // {
264 | // id,
265 | // location: {
266 | // name: location,
267 | // country: country,
268 | // },
269 | // description,
270 | // distance: {
271 | // km: 0,
272 | // },
273 | // price: {
274 | // perNight: price,
275 | // },
276 | // rating: 5,
277 | // imageURL,
278 | // },
279 | // ])
280 | // }
281 |
282 |
283 | return { airbnbs, bookings, addAirbnb,updateAirbnb, removeAirbnb,bookAirbnb, cancelBooking, initializeUser , initialized , loading, transactionPending}
284 | }
285 |
--------------------------------------------------------------------------------