sina_sajjadi
2 months ago
18 changed files with 1354 additions and 381 deletions
-
120package-lock.json
-
1package.json
-
7src/app/(client-components)/(Header)/MainNav1.tsx
-
65src/app/(stay-listings)/SectionGridFilterCard.tsx
-
21src/app/(stay-listings)/SectionGridHasMap.tsx
-
155src/app/(stay-listings)/TabFilters.tsx
-
6src/app/(stay-listings)/layout.tsx
-
3src/app/(stay-listings)/listing-stay-map/page.tsx
-
111src/app/custom-history/page.tsx
-
340src/app/custom-trip/page.tsx
-
3src/app/layout.tsx
-
83src/app/tours/SectionGridFilterCard.tsx
-
690src/app/tours/TabFilters.tsx
-
14src/app/tours/page.tsx
-
2src/components/CardCategory3.tsx
-
9src/components/SectionGridFeaturePlaces.tsx
-
20src/shared/Checkbox.tsx
-
85yarn.lock
@ -0,0 +1,111 @@ |
|||
'use client'; |
|||
|
|||
import React, { FC, useEffect, useState } from 'react'; |
|||
import axiosInstance from '@/components/api/axios'; |
|||
import { TrashIcon } from '@heroicons/react/24/outline'; // Import the Trash icon for the delete button
|
|||
import { FaWhatsapp } from "react-icons/fa"; |
|||
|
|||
|
|||
export interface PageAddListing10Props {} |
|||
|
|||
const PageAddListing10: FC<PageAddListing10Props> = () => { |
|||
const [tours, setTours] = useState([]); |
|||
const [toursDetail, setToursDetail] = useState([]); |
|||
const [user, setUser] = useState(null); |
|||
|
|||
useEffect(() => { |
|||
const userData = localStorage.getItem('user'); |
|||
if (userData) { |
|||
const parsedUser = JSON.parse(userData); |
|||
setUser(parsedUser); |
|||
|
|||
axiosInstance('/api/trip/custom/orders/', { |
|||
headers: { |
|||
Authorization: `token ${parsedUser.token}`, |
|||
}, |
|||
}) |
|||
.then((response) => { |
|||
setTours(response.data.results); |
|||
console.log(response); |
|||
|
|||
|
|||
// Parse tour details
|
|||
const details = response.data.results.map((item) => |
|||
JSON.parse(item.detail) |
|||
); |
|||
setToursDetail(details); |
|||
}) |
|||
.catch((error) => { |
|||
console.log(error); |
|||
}); |
|||
} |
|||
}, []); |
|||
|
|||
const deleteTour = (tour)=>{ |
|||
const selected = tours.find((item)=>{ |
|||
return JSON.parse(item.detail) === tour |
|||
}) |
|||
console.log(JSON.parse(tours[0].detail ) == tour.title[1].title ,JSON.parse(tours[0].detail ) , tour) |
|||
|
|||
} |
|||
console.log(tours); |
|||
|
|||
return ( |
|||
<div className="px-4 max-w-md mx-auto pb-24 pt-14 sm:py-24 lg:pb-32"> |
|||
<div className="flex items-center justify-between mb-4"> |
|||
<h2 className="text-2xl font-semibold">Custom Trip History</h2> |
|||
<span className="text-gray-500 text-sm">2023 Dec 02 | 16:17</span> |
|||
</div> |
|||
<p className="text-green-600 text-sm mb-4">Successfully Registered</p> |
|||
{toursDetail.length > 0 ? ( |
|||
toursDetail.map((item, index) => ( |
|||
<div |
|||
key={index} |
|||
className="bg-white border border-gray-200 rounded-3xl p-4 mb-4 shadow-md" |
|||
> |
|||
<div className="flex flex-col mb-2"> |
|||
<div className='flex justify-between'> |
|||
<p className="font-semibold text-gray-700">Origin:</p> |
|||
<p className="text-gray-900">{item['1']?.city || 'N/A'}</p> |
|||
</div> |
|||
|
|||
{Object.keys(item) |
|||
.filter((key) => !isNaN(Number(key)) && Number(key) > 1) |
|||
.map((key) => ( |
|||
<div className='flex justify-between' key={key}> |
|||
<p className="font-semibold text-gray-700">{item[key].title}:</p> |
|||
<p className="text-gray-900">{item[key]?.city || 'N/A'}</p> |
|||
</div> |
|||
))} |
|||
</div> |
|||
|
|||
<div className="flex items-center justify-between"> |
|||
<div> |
|||
<p className=" text-gray-600">Estimated Cost: <span className="">${item.summary?.cost_estimate || 'N/A'}</span></p> |
|||
|
|||
</div> |
|||
<button onClick={()=>{deleteTour(item)}} className="flex items-center text-red-500 hover:text-red-700"> |
|||
<TrashIcon className="w-5 h-5 mr-1" /> |
|||
Delete |
|||
{console.log(item) |
|||
} |
|||
</button> |
|||
</div> |
|||
</div> |
|||
)) |
|||
) : ( |
|||
<p className="text-center text-gray-500">No tours available.</p> |
|||
)} |
|||
|
|||
<div className="flex justify-between mt-8 items-center"> |
|||
<p className="font-semibold text-gray-700 mb-2">Contact Support</p> |
|||
<button className="flex items-center justify-center bg-[#032935] rounded-full text-white p-3"> |
|||
<FaWhatsapp className='w-9' /> |
|||
Whatsapp |
|||
</button> |
|||
</div> |
|||
</div> |
|||
); |
|||
}; |
|||
|
|||
export default PageAddListing10; |
@ -0,0 +1,83 @@ |
|||
"use client"; |
|||
|
|||
import React, { FC, useContext, useEffect, useState } from "react"; |
|||
import { DEMO_STAY_LISTINGS } from "@/data/listings"; |
|||
import { StayDataType } from "@/data/types"; |
|||
import TabFilters from "./TabFilters"; |
|||
import Heading2 from "@/shared/Heading2"; |
|||
import StayCard2 from "@/components/StayCard2"; |
|||
import { Context } from "@/components/contexts/tourDetails"; |
|||
import { useParams, useSearchParams } from "next/navigation"; |
|||
import { useRouter } from "next/navigation"; |
|||
|
|||
export interface SectionGridFilterCardProps { |
|||
className?: string; |
|||
data?: StayDataType[]; |
|||
} |
|||
|
|||
const SectionGridFilterCard: FC<SectionGridFilterCardProps> = ({ |
|||
className = "", |
|||
data = DEMO_STAY_LISTINGS, |
|||
}) => { |
|||
const { countries, tours } = useContext(Context); |
|||
const [countryTours, setCountryTours] = useState(tours.results || []); |
|||
const [checked, setChecked] = useState<{ [key: string]: boolean }>({}); |
|||
|
|||
const searchParams = useSearchParams() |
|||
console.log(searchParams); |
|||
|
|||
// Get the list of selected countries
|
|||
const filteredCountries = Object.keys(checked).filter((countryName) => checked[countryName]); |
|||
|
|||
useEffect(()=>{ |
|||
const country = searchParams.get("country") |
|||
if (searchParams.has("country")){ |
|||
setChecked({ |
|||
[country] : true |
|||
}) |
|||
} |
|||
} , []) |
|||
console.log(checked); |
|||
|
|||
|
|||
|
|||
useEffect(() => { |
|||
if (!tours.results) return; |
|||
|
|||
if (filteredCountries.length === 0) { |
|||
// If no country is selected, show all tours
|
|||
setCountryTours(tours.results); |
|||
} else { |
|||
// Map selected country names to country codes
|
|||
const selectedCountryCodes = countries |
|||
.filter((country) => filteredCountries.includes(country.name)) |
|||
.map((country) => country.code); |
|||
|
|||
// Filter tours based on selected country codes
|
|||
const filteredTours = tours.results.filter((tour) => |
|||
selectedCountryCodes.includes(tour.destination_country) |
|||
); |
|||
|
|||
setCountryTours(filteredTours); |
|||
} |
|||
}, [checked, countries, tours.results]); |
|||
|
|||
return ( |
|||
<div className={`nc-SectionGridFilterCard container ${className}`} data-nc-id="SectionGridFilterCard"> |
|||
<Heading2 /> |
|||
|
|||
<div className="mb-8 lg:mb-11"> |
|||
<TabFilters onChangeCountry={setChecked} data={countries} /> |
|||
</div> |
|||
<div className="grid grid-cols-1 gap-6 md:gap-8 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4"> |
|||
{countryTours.length > 0 ? ( |
|||
countryTours.map((stay) => <StayCard2 key={stay.id} data={stay} />) |
|||
) : ( |
|||
<h2>No tours Available</h2> |
|||
)} |
|||
</div> |
|||
</div> |
|||
); |
|||
}; |
|||
|
|||
export default SectionGridFilterCard; |
@ -0,0 +1,690 @@ |
|||
"use client"; |
|||
|
|||
import React, { Fragment, useEffect, useState } from "react"; |
|||
import { Dialog, Popover, Transition } from "@headlessui/react"; |
|||
import NcInputNumber from "@/components/NcInputNumber"; |
|||
import ButtonPrimary from "@/shared/ButtonPrimary"; |
|||
import ButtonThird from "@/shared/ButtonThird"; |
|||
import ButtonClose from "@/shared/ButtonClose"; |
|||
import Checkbox from "@/shared/Checkbox"; |
|||
import Slider from "rc-slider"; |
|||
import convertNumbThousand from "@/utils/convertNumbThousand"; |
|||
|
|||
// DEMO DATA
|
|||
const typeOfPaces = [ |
|||
{ |
|||
name: "Entire place", |
|||
description: "Have a place to yourself", |
|||
}, |
|||
{ |
|||
name: "Private room", |
|||
description: "Have your own room and share some common spaces", |
|||
}, |
|||
{ |
|||
name: "Hotel room", |
|||
description: |
|||
"Have a private or shared room in a boutique hotel, hostel, and more", |
|||
}, |
|||
{ |
|||
name: "Shared room", |
|||
description: "Stay in a shared space, like a common room", |
|||
}, |
|||
]; |
|||
|
|||
const moreFilter1 = [ |
|||
{ name: "Kitchen", defaultChecked: true }, |
|||
{ name: "Air conditioning", defaultChecked: true }, |
|||
{ name: "Heating" }, |
|||
{ name: "Dryer" }, |
|||
{ name: "Washer" }, |
|||
{ name: "Wifi" }, |
|||
{ name: "Indoor fireplace" }, |
|||
{ name: "Breakfast" }, |
|||
{ name: "Hair dryer" }, |
|||
{ name: " Dedicated workspace" }, |
|||
]; |
|||
|
|||
const moreFilter2 = [ |
|||
{ name: " Free parking on premise" }, |
|||
{ name: "Hot tub" }, |
|||
{ name: "Gym" }, |
|||
{ name: " Pool" }, |
|||
{ name: " EV charger" }, |
|||
]; |
|||
|
|||
const moreFilter3 = [ |
|||
{ name: " House" }, |
|||
{ name: "Bed and breakfast" }, |
|||
{ name: "Apartment", defaultChecked: true }, |
|||
{ name: " Boutique hotel" }, |
|||
{ name: " Bungalow" }, |
|||
{ name: " Chalet", defaultChecked: true }, |
|||
{ name: " Condominium", defaultChecked: true }, |
|||
{ name: " Cottage" }, |
|||
{ name: " Guest suite" }, |
|||
{ name: " Guesthouse" }, |
|||
]; |
|||
|
|||
const moreFilter4 = [{ name: " Pets allowed" }, { name: "Smoking allowed" }]; |
|||
|
|||
const TabFilters = ({data , |
|||
onChangeCountry = (item)=>{} |
|||
|
|||
}) => { |
|||
const [isOpenMoreFilter, setisOpenMoreFilter] = useState(false); |
|||
const [isOpenMoreFilterMobile, setisOpenMoreFilterMobile] = useState(false); |
|||
const [rangePrices, setRangePrices] = useState([0, 1000]); |
|||
const [checkedItems, setCheckedItems] = useState({}); |
|||
|
|||
useEffect(()=>{ |
|||
onChangeCountry(checkedItems) |
|||
} , [checkedItems]) |
|||
|
|||
//
|
|||
const closeModalMoreFilter = () => setisOpenMoreFilter(false); |
|||
const openModalMoreFilter = () => setisOpenMoreFilter(true); |
|||
//
|
|||
const closeModalMoreFilterMobile = () => setisOpenMoreFilterMobile(false); |
|||
const openModalMoreFilterMobile = () => setisOpenMoreFilterMobile(true); |
|||
console.log(data); |
|||
|
|||
const renderXClear = () => { |
|||
return ( |
|||
<span className="w-4 h-4 rounded-full bg-primary-500 text-white flex items-center justify-center ml-3 cursor-pointer"> |
|||
<svg |
|||
xmlns="http://www.w3.org/2000/svg" |
|||
className="h-3 w-3" |
|||
viewBox="0 0 20 20" |
|||
fill="currentColor" |
|||
> |
|||
<path |
|||
fillRule="evenodd" |
|||
d="M4.293 4.293a1 1 0 011.414 0L10 8.586l4.293-4.293a1 1 0 111.414 1.414L11.414 10l4.293 4.293a1 1 0 01-1.414 1.414L10 11.414l-4.293 4.293a1 1 0 01-1.414-1.414L8.586 10 4.293 5.707a1 1 0 010-1.414z" |
|||
clipRule="evenodd" |
|||
/> |
|||
</svg> |
|||
</span> |
|||
); |
|||
}; |
|||
|
|||
const renderTabsTypeOfPlace = () => { |
|||
return ( |
|||
<Popover className="relative"> |
|||
{({ open, close }) => ( |
|||
<> |
|||
<Popover.Button |
|||
className={`flex items-center justify-center px-4 py-2 text-sm rounded-full border border-neutral-300 dark:border-neutral-700 hover:border-neutral-400 dark:hover:border-neutral-6000 focus:outline-none ${ |
|||
open ? "!border-primary-500 " : "" |
|||
}`}
|
|||
> |
|||
<span>Type of place</span> |
|||
<i className="las la-angle-down ml-2"></i> |
|||
</Popover.Button> |
|||
<Transition |
|||
as={Fragment} |
|||
// ... transition props
|
|||
> |
|||
<Popover.Panel className="absolute z-10 w-screen max-w-sm px-4 mt-3 left-0 sm:px-0 lg:max-w-md"> |
|||
<div className="overflow-hidden rounded-2xl shadow-xl bg-white dark:bg-neutral-900 border border-neutral-200 dark:border-neutral-700"> |
|||
<div className="relative flex flex-col px-5 py-6 space-y-5"> |
|||
{data.map((item) => ( |
|||
<div key={item.id} className=""> |
|||
<Checkbox |
|||
name={item.name} |
|||
label={item.name} |
|||
checked={!!checkedItems[item.name]} |
|||
onChange={(checked) => { |
|||
setCheckedItems((prev) => ({ |
|||
...prev, |
|||
[item.name]: checked, |
|||
})); |
|||
}} |
|||
/> |
|||
</div> |
|||
))} |
|||
</div> |
|||
</div> |
|||
</Popover.Panel> |
|||
</Transition> |
|||
</> |
|||
)} |
|||
</Popover> |
|||
); |
|||
}; |
|||
|
|||
const renderTabsRoomAndBeds = () => { |
|||
return ( |
|||
<Popover className="relative"> |
|||
{({ open, close }) => ( |
|||
<> |
|||
<Popover.Button |
|||
className={`flex items-center justify-center px-4 py-2 text-sm rounded-full border border-neutral-300 dark:border-neutral-700 hover:border-neutral-400 dark:hover:border-neutral-6000 focus:outline-none ${ |
|||
open ? "!border-primary-500 " : "" |
|||
}`}
|
|||
> |
|||
<span>Rooms of Beds</span> |
|||
<i className="las la-angle-down ml-2"></i> |
|||
</Popover.Button> |
|||
<Transition |
|||
as={Fragment} |
|||
enter="transition ease-out duration-200" |
|||
enterFrom="opacity-0 translate-y-1" |
|||
enterTo="opacity-100 translate-y-0" |
|||
leave="transition ease-in duration-150" |
|||
leaveFrom="opacity-100 translate-y-0" |
|||
leaveTo="opacity-0 translate-y-1" |
|||
> |
|||
<Popover.Panel className="absolute z-10 w-screen max-w-sm px-4 mt-3 left-0 sm:px-0 lg:max-w-md"> |
|||
<div className="overflow-hidden rounded-2xl shadow-xl bg-white dark:bg-neutral-900 border border-neutral-200 dark:border-neutral-700"> |
|||
<div className="relative flex flex-col px-5 py-6 space-y-5"> |
|||
<NcInputNumber label="Beds" max={10} /> |
|||
<NcInputNumber label="Bedrooms" max={10} /> |
|||
<NcInputNumber label="Bathrooms" max={10} /> |
|||
</div> |
|||
<div className="p-5 bg-neutral-50 dark:bg-neutral-900 dark:border-t dark:border-neutral-800 flex items-center justify-between"> |
|||
<ButtonThird onClick={close} sizeClass="px-4 py-2 sm:px-5"> |
|||
Clear |
|||
</ButtonThird> |
|||
<ButtonPrimary |
|||
onClick={close} |
|||
sizeClass="px-4 py-2 sm:px-5" |
|||
> |
|||
Apply |
|||
</ButtonPrimary> |
|||
</div> |
|||
</div> |
|||
</Popover.Panel> |
|||
</Transition> |
|||
</> |
|||
)} |
|||
</Popover> |
|||
); |
|||
}; |
|||
|
|||
const renderTabsPriceRage = () => { |
|||
return ( |
|||
<Popover className="relative"> |
|||
{({ open, close }) => ( |
|||
<> |
|||
<Popover.Button |
|||
className={`flex items-center justify-center px-4 py-2 text-sm rounded-full border border-primary-500 bg-primary-50 text-primary-700 focus:outline-none `} |
|||
> |
|||
<span> |
|||
{`$${convertNumbThousand( |
|||
rangePrices[0] |
|||
)} - $${convertNumbThousand(rangePrices[1])}`}{" "}
|
|||
</span> |
|||
{renderXClear()} |
|||
</Popover.Button> |
|||
<Transition |
|||
as={Fragment} |
|||
enter="transition ease-out duration-200" |
|||
enterFrom="opacity-0 translate-y-1" |
|||
enterTo="opacity-100 translate-y-0" |
|||
leave="transition ease-in duration-150" |
|||
leaveFrom="opacity-100 translate-y-0" |
|||
leaveTo="opacity-0 translate-y-1" |
|||
> |
|||
<Popover.Panel className="absolute z-10 w-screen max-w-sm px-4 mt-3 left-0 sm:px-0 "> |
|||
<div className="overflow-hidden rounded-2xl shadow-xl bg-white dark:bg-neutral-900 border border-neutral-200 dark:border-neutral-700"> |
|||
<div className="relative flex flex-col px-5 py-6 space-y-8"> |
|||
<div className="space-y-5"> |
|||
<span className="font-medium">Price per day</span> |
|||
<Slider |
|||
range |
|||
className="text-red-400" |
|||
min={0} |
|||
max={2000} |
|||
defaultValue={[rangePrices[0], rangePrices[1]]} |
|||
allowCross={false} |
|||
onChange={(e) => setRangePrices(e as number[])} |
|||
/> |
|||
</div> |
|||
|
|||
<div className="flex justify-between space-x-5"> |
|||
<div> |
|||
<label |
|||
htmlFor="minPrice" |
|||
className="block text-sm font-medium text-neutral-700 dark:text-neutral-300" |
|||
> |
|||
Min price |
|||
</label> |
|||
<div className="mt-1 relative rounded-md"> |
|||
<div className="absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none"> |
|||
<span className="text-neutral-500 sm:text-sm"> |
|||
$ |
|||
</span> |
|||
</div> |
|||
<input |
|||
type="text" |
|||
name="minPrice" |
|||
disabled |
|||
id="minPrice" |
|||
className="focus:ring-indigo-500 focus:border-indigo-500 block w-full pl-7 pr-3 sm:text-sm border-neutral-200 rounded-full text-neutral-900" |
|||
value={rangePrices[0]} |
|||
/> |
|||
</div> |
|||
</div> |
|||
<div> |
|||
<label |
|||
htmlFor="maxPrice" |
|||
className="block text-sm font-medium text-neutral-700 dark:text-neutral-300" |
|||
> |
|||
Max price |
|||
</label> |
|||
<div className="mt-1 relative rounded-md"> |
|||
<div className="absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none"> |
|||
<span className="text-neutral-500 sm:text-sm"> |
|||
$ |
|||
</span> |
|||
</div> |
|||
<input |
|||
type="text" |
|||
disabled |
|||
name="maxPrice" |
|||
id="maxPrice" |
|||
className="focus:ring-indigo-500 focus:border-indigo-500 block w-full pl-7 pr-3 sm:text-sm border-neutral-200 rounded-full text-neutral-900" |
|||
value={rangePrices[1]} |
|||
/> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
<div className="p-5 bg-neutral-50 dark:bg-neutral-900 dark:border-t dark:border-neutral-800 flex items-center justify-between"> |
|||
<ButtonThird onClick={close} sizeClass="px-4 py-2 sm:px-5"> |
|||
Clear |
|||
</ButtonThird> |
|||
<ButtonPrimary |
|||
onClick={close} |
|||
sizeClass="px-4 py-2 sm:px-5" |
|||
> |
|||
Apply |
|||
</ButtonPrimary> |
|||
</div> |
|||
</div> |
|||
</Popover.Panel> |
|||
</Transition> |
|||
</> |
|||
)} |
|||
</Popover> |
|||
); |
|||
}; |
|||
|
|||
// Inside TabFilters component
|
|||
const renderMoreFilterItem = ( |
|||
data: { |
|||
name: string; |
|||
defaultChecked?: boolean; |
|||
}[] |
|||
) => { |
|||
const list1 = data.filter((_, i) => i < data.length / 2); |
|||
const list2 = data.filter((_, i) => i >= data.length / 2); |
|||
return ( |
|||
<div className="grid grid-cols-2 gap-8"> |
|||
<div className="flex flex-col space-y-5"> |
|||
{list1.map((item) => ( |
|||
<Checkbox |
|||
key={item.name} |
|||
name={item.name} |
|||
label={item.name} |
|||
checked={!!checkedItems[item.name]} |
|||
onChange={(checked) => { |
|||
setCheckedItems((prev) => ({ |
|||
...prev, |
|||
[item.name]: checked, |
|||
})); |
|||
}} |
|||
/> |
|||
))} |
|||
</div> |
|||
<div className="flex flex-col space-y-5"> |
|||
{list2.map((item) => ( |
|||
<Checkbox |
|||
key={item.name} |
|||
name={item.name} |
|||
label={item.name} |
|||
checked={!!checkedItems[item.name]} |
|||
onChange={(checked) => { |
|||
setCheckedItems((prev) => ({ |
|||
...prev, |
|||
[item.name]: checked, |
|||
})); |
|||
}} |
|||
/> |
|||
))} |
|||
</div> |
|||
</div> |
|||
); |
|||
}; |
|||
|
|||
|
|||
const renderTabMoreFilter = () => { |
|||
return ( |
|||
<div> |
|||
<div |
|||
className={`flex items-center justify-center px-4 py-2 text-sm rounded-full border border-primary-500 bg-primary-50 text-primary-700 focus:outline-none cursor-pointer`} |
|||
onClick={openModalMoreFilter} |
|||
> |
|||
<span>More filters (3)</span> |
|||
{renderXClear()} |
|||
</div> |
|||
|
|||
<Transition appear show={isOpenMoreFilter} as={Fragment}> |
|||
<Dialog |
|||
as="div" |
|||
className="fixed inset-0 z-50 overflow-y-auto" |
|||
onClose={closeModalMoreFilter} |
|||
> |
|||
<div className="min-h-screen text-center"> |
|||
<Transition.Child |
|||
as={Fragment} |
|||
enter="ease-out duration-300" |
|||
enterFrom="opacity-0" |
|||
enterTo="opacity-100" |
|||
leave="ease-in duration-200" |
|||
leaveFrom="opacity-100" |
|||
leaveTo="opacity-0" |
|||
> |
|||
<Dialog.Overlay className="fixed inset-0 bg-black bg-opacity-40 dark:bg-opacity-60" /> |
|||
</Transition.Child> |
|||
|
|||
{/* This element is to trick the browser into centering the modal contents. */} |
|||
<span |
|||
className="inline-block h-screen align-middle" |
|||
aria-hidden="true" |
|||
> |
|||
​ |
|||
</span> |
|||
<Transition.Child |
|||
className="inline-block py-8 px-2 h-screen w-full max-w-4xl" |
|||
enter="ease-out duration-300" |
|||
enterFrom="opacity-0 scale-95" |
|||
enterTo="opacity-100 scale-100" |
|||
leave="ease-in duration-200" |
|||
leaveFrom="opacity-100 scale-100" |
|||
leaveTo="opacity-0 scale-95" |
|||
> |
|||
<div className="inline-flex flex-col w-full max-w-4xl text-left align-middle transition-all transform overflow-hidden rounded-2xl bg-white dark:bg-neutral-900 dark:border dark:border-neutral-700 dark:text-neutral-100 shadow-xl h-full"> |
|||
<div className="relative flex-shrink-0 px-6 py-4 border-b border-neutral-200 dark:border-neutral-800 text-center"> |
|||
<Dialog.Title |
|||
as="h3" |
|||
className="text-lg font-medium leading-6 text-gray-900" |
|||
> |
|||
More filters |
|||
</Dialog.Title> |
|||
<span className="absolute left-3 top-3"> |
|||
<ButtonClose onClick={closeModalMoreFilter} /> |
|||
</span> |
|||
</div> |
|||
|
|||
<div className="flex-grow overflow-y-auto"> |
|||
<div className="px-10 divide-y divide-neutral-200 dark:divide-neutral-800"> |
|||
<div className="py-7"> |
|||
<h3 className="text-xl font-medium">Amenities</h3> |
|||
<div className="mt-6 relative "> |
|||
{renderMoreFilterItem(moreFilter1)} |
|||
</div> |
|||
</div> |
|||
<div className="py-7"> |
|||
<h3 className="text-xl font-medium">Facilities</h3> |
|||
<div className="mt-6 relative "> |
|||
{renderMoreFilterItem(moreFilter2)} |
|||
</div> |
|||
</div> |
|||
<div className="py-7"> |
|||
<h3 className="text-xl font-medium">Property type</h3> |
|||
<div className="mt-6 relative "> |
|||
{renderMoreFilterItem(moreFilter3)} |
|||
</div> |
|||
</div> |
|||
<div className="py-7"> |
|||
<h3 className="text-xl font-medium">House rules</h3> |
|||
<div className="mt-6 relative "> |
|||
{renderMoreFilterItem(moreFilter4)} |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
|
|||
<div className="p-6 flex-shrink-0 bg-neutral-50 dark:bg-neutral-900 dark:border-t dark:border-neutral-800 flex items-center justify-between"> |
|||
<ButtonThird |
|||
onClick={closeModalMoreFilter} |
|||
sizeClass="px-4 py-2 sm:px-5" |
|||
> |
|||
Clear |
|||
</ButtonThird> |
|||
<ButtonPrimary |
|||
onClick={closeModalMoreFilter} |
|||
sizeClass="px-4 py-2 sm:px-5" |
|||
> |
|||
Apply |
|||
</ButtonPrimary> |
|||
</div> |
|||
</div> |
|||
</Transition.Child> |
|||
</div> |
|||
</Dialog> |
|||
</Transition> |
|||
</div> |
|||
); |
|||
}; |
|||
|
|||
const renderTabMoreFilterMobile = () => { |
|||
return ( |
|||
<div> |
|||
<div |
|||
className={`flex lg:hidden items-center justify-center px-4 py-2 text-sm rounded-full border border-primary-500 bg-primary-50 text-primary-700 focus:outline-none cursor-pointer`} |
|||
onClick={openModalMoreFilterMobile} |
|||
> |
|||
<span>Countries</span> |
|||
{renderXClear()} |
|||
</div> |
|||
|
|||
<Transition appear show={isOpenMoreFilterMobile} as={Fragment}> |
|||
<Dialog |
|||
as="div" |
|||
className="fixed inset-0 z-50 overflow-y-auto" |
|||
onClose={closeModalMoreFilterMobile} |
|||
> |
|||
<div className="min-h-screen text-center"> |
|||
<Transition.Child |
|||
as={Fragment} |
|||
enter="ease-out duration-300" |
|||
enterFrom="opacity-0" |
|||
enterTo="opacity-100" |
|||
leave="ease-in duration-200" |
|||
leaveFrom="opacity-100" |
|||
leaveTo="opacity-0" |
|||
> |
|||
<Dialog.Overlay className="fixed inset-0 bg-black bg-opacity-40 dark:bg-opacity-60" /> |
|||
</Transition.Child> |
|||
|
|||
{/* This element is to trick the browser into centering the modal contents. */} |
|||
<span |
|||
className="inline-block h-screen align-middle" |
|||
aria-hidden="true" |
|||
> |
|||
​ |
|||
</span> |
|||
<Transition.Child |
|||
className="inline-block py-8 px-2 h-screen w-full max-w-4xl" |
|||
enter="ease-out duration-300" |
|||
enterFrom="opacity-0 scale-95" |
|||
enterTo="opacity-100 scale-100" |
|||
leave="ease-in duration-200" |
|||
leaveFrom="opacity-100 scale-100" |
|||
leaveTo="opacity-0 scale-95" |
|||
> |
|||
<div className="inline-flex flex-col w-full max-w-4xl text-left align-middle transition-all transform overflow-hidden rounded-2xl bg-white dark:bg-neutral-900 dark:border dark:border-neutral-700 dark:text-neutral-100 shadow-xl h-full"> |
|||
<div className="relative flex-shrink-0 px-6 py-4 border-b border-neutral-200 dark:border-neutral-800 text-center"> |
|||
<Dialog.Title |
|||
as="h3" |
|||
className="text-lg font-medium leading-6 text-gray-900" |
|||
> |
|||
More filters |
|||
</Dialog.Title> |
|||
<span className="absolute left-3 top-3"> |
|||
<ButtonClose onClick={closeModalMoreFilterMobile} /> |
|||
</span> |
|||
</div> |
|||
|
|||
<div className="flex-grow overflow-y-auto"> |
|||
<div className="px-4 sm:px-6 divide-y divide-neutral-200 dark:divide-neutral-800"> |
|||
{/* ---- */} |
|||
<div className="py-7"> |
|||
<h3 className="text-xl font-medium">Type of place</h3> |
|||
<div className="mt-6 relative "> |
|||
{renderMoreFilterItem(data)} |
|||
</div> |
|||
</div> |
|||
|
|||
{/* ---- */} |
|||
{/* <div className="py-7"> |
|||
<h3 className="text-xl font-medium">Range Prices</h3> |
|||
<div className="mt-6 relative "> |
|||
<div className="relative flex flex-col space-y-8"> |
|||
<div className="space-y-5"> |
|||
<Slider |
|||
range |
|||
className="text-red-400" |
|||
min={0} |
|||
max={2000} |
|||
defaultValue={[0, 1000]} |
|||
allowCross={false} |
|||
onChange={(e) => setRangePrices(e as number[])} |
|||
/> |
|||
</div> |
|||
|
|||
<div className="flex justify-between space-x-5"> |
|||
<div> |
|||
<label |
|||
htmlFor="minPrice" |
|||
className="block text-sm font-medium text-neutral-700 dark:text-neutral-300" |
|||
> |
|||
Min price |
|||
</label> |
|||
<div className="mt-1 relative rounded-md"> |
|||
<div className="absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none"> |
|||
<span className="text-neutral-500 sm:text-sm"> |
|||
$ |
|||
</span> |
|||
</div> |
|||
<input |
|||
type="text" |
|||
name="minPrice" |
|||
disabled |
|||
id="minPrice" |
|||
className="focus:ring-indigo-500 focus:border-indigo-500 block w-full pl-7 pr-3 sm:text-sm border-neutral-200 rounded-full text-neutral-900" |
|||
value={rangePrices[0]} |
|||
/> |
|||
</div> |
|||
</div> |
|||
<div> |
|||
<label |
|||
htmlFor="maxPrice" |
|||
className="block text-sm font-medium text-neutral-700 dark:text-neutral-300" |
|||
> |
|||
Max price |
|||
</label> |
|||
<div className="mt-1 relative rounded-md"> |
|||
<div className="absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none"> |
|||
<span className="text-neutral-500 sm:text-sm"> |
|||
$ |
|||
</span> |
|||
</div> |
|||
<input |
|||
type="text" |
|||
disabled |
|||
name="maxPrice" |
|||
id="maxPrice" |
|||
className="focus:ring-indigo-500 focus:border-indigo-500 block w-full pl-7 pr-3 sm:text-sm border-neutral-200 rounded-full text-neutral-900" |
|||
value={rangePrices[1]} |
|||
/> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</div> */} |
|||
|
|||
{/* ---- */} |
|||
{/* <div className="py-7"> |
|||
<h3 className="text-xl font-medium">Rooms and beds</h3> |
|||
<div className="mt-6 relative flex flex-col space-y-5"> |
|||
<NcInputNumber label="Beds" max={10} /> |
|||
<NcInputNumber label="Bedrooms" max={10} /> |
|||
<NcInputNumber label="Bathrooms" max={10} /> |
|||
</div> |
|||
</div> */} |
|||
|
|||
{/* ---- */} |
|||
{/* <div className="py-7"> |
|||
<h3 className="text-xl font-medium">Amenities</h3> |
|||
<div className="mt-6 relative "> |
|||
{renderMoreFilterItem(moreFilter1)} |
|||
</div> |
|||
</div> */} |
|||
|
|||
{/* ---- */} |
|||
{/* <div className="py-7"> |
|||
<h3 className="text-xl font-medium">Facilities</h3> |
|||
<div className="mt-6 relative "> |
|||
{renderMoreFilterItem(moreFilter2)} |
|||
</div> |
|||
</div> */} |
|||
|
|||
{/* ---- */} |
|||
{/* <div className="py-7"> |
|||
<h3 className="text-xl font-medium">Property type</h3> |
|||
<div className="mt-6 relative "> |
|||
{renderMoreFilterItem(moreFilter3)} |
|||
</div> |
|||
</div> */} |
|||
|
|||
{/* ---- */} |
|||
{/* <div className="py-7"> |
|||
<h3 className="text-xl font-medium">House rules</h3> |
|||
<div className="mt-6 relative "> |
|||
{renderMoreFilterItem(moreFilter4)} |
|||
</div> |
|||
</div> */} |
|||
</div> |
|||
</div> |
|||
|
|||
<div className="p-4 sm:p-6 flex-shrink-0 bg-neutral-50 dark:bg-neutral-900 dark:border-t dark:border-neutral-800 flex items-center justify-between"> |
|||
<ButtonThird |
|||
onClick={closeModalMoreFilterMobile} |
|||
sizeClass="px-4 py-2 sm:px-5" |
|||
> |
|||
Clear |
|||
</ButtonThird> |
|||
<ButtonPrimary |
|||
onClick={closeModalMoreFilterMobile} |
|||
sizeClass="px-4 py-2 sm:px-5" |
|||
> |
|||
Apply |
|||
</ButtonPrimary> |
|||
</div> |
|||
</div> |
|||
</Transition.Child> |
|||
</div> |
|||
</Dialog> |
|||
</Transition> |
|||
</div> |
|||
); |
|||
}; |
|||
|
|||
return ( |
|||
<div className="flex lg:space-x-4"> |
|||
<div className="hidden lg:flex space-x-4"> |
|||
{renderTabsTypeOfPlace()} |
|||
{/* {renderTabsPriceRage()} */} |
|||
{/* {renderTabsRoomAndBeds()} */} |
|||
{/* {renderTabMoreFilter()} */} |
|||
</div> |
|||
{renderTabMoreFilterMobile()} |
|||
</div> |
|||
); |
|||
}; |
|||
|
|||
export default TabFilters; |
@ -0,0 +1,14 @@ |
|||
import React, { FC } from "react"; |
|||
import SectionGridFilterCard from "./SectionGridFilterCard"; |
|||
|
|||
export interface ListingStayMapPageProps {} |
|||
|
|||
const ListingStayMapPage: FC<ListingStayMapPageProps> = ({}) => { |
|||
return ( |
|||
<div className="container pb-24 lg:pb-28 2xl:pl-10 xl:pr-0 xl:max-w-none"> |
|||
<SectionGridFilterCard /> |
|||
</div> |
|||
); |
|||
}; |
|||
|
|||
export default ListingStayMapPage; |
Write
Preview
Loading…
Cancel
Save
Reference in new issue