Browse Source
feat : age label added to passengers
feat : age label added to passengers
-API is not connected feat : bills page UI addedmain
sina_sajjadi
2 weeks ago
62 changed files with 783 additions and 6747 deletions
-
2next.config.js
-
1src/app/(account-pages)/(components)/Nav.tsx
-
68src/app/(account-pages)/bills/BillCard.tsx
-
113src/app/(account-pages)/bills/[slug]/page.tsx
-
106src/app/(account-pages)/bills/page.tsx
-
48src/app/(car-listings)/SectionGridFilterCard.tsx
-
112src/app/(car-listings)/SectionGridHasMap.tsx
-
683src/app/(car-listings)/TabFilters.tsx
-
59src/app/(car-listings)/layout.tsx
-
14src/app/(car-listings)/listing-car-map/page.tsx
-
14src/app/(car-listings)/listing-car/page.tsx
-
1src/app/(client-components)/(Header)/SiteHeader.tsx
-
134src/app/(client-components)/(HeroSearchForm)/(car-search-form)/RentalCarDatesRangeInput.tsx
-
70src/app/(client-components)/(HeroSearchForm)/(car-search-form)/RentalCarSearchForm.tsx
-
119src/app/(client-components)/(HeroSearchForm)/(experiences-search-form)/ExperiencesDateSingleInput.tsx
-
27src/app/(client-components)/(HeroSearchForm)/(experiences-search-form)/ExperiencesSearchForm.tsx
-
152src/app/(client-components)/(HeroSearchForm)/(flight-search-form)/FlightDateRangeInput.tsx
-
246src/app/(client-components)/(HeroSearchForm)/(flight-search-form)/FlightSearchForm.tsx
-
66src/app/(client-components)/(HeroSearchForm)/(real-estate-search-form)/HeroRealEstateSearchForm.tsx
-
137src/app/(client-components)/(HeroSearchForm)/(real-estate-search-form)/PriceRangeInput.tsx
-
126src/app/(client-components)/(HeroSearchForm)/(real-estate-search-form)/PropertyTypeSelect.tsx
-
25src/app/(client-components)/(HeroSearchForm)/(real-estate-search-form)/RealEstateSearchForm.tsx
-
71src/app/(client-components)/(HeroSearchForm)/GuestsInput.tsx
-
129src/app/(client-components)/(HeroSearchFormSmall)/(car-search-form)/RentalCarDatesRangeInput.tsx
-
72src/app/(client-components)/(HeroSearchFormSmall)/(car-search-form)/RentalCarSearchForm.tsx
-
114src/app/(client-components)/(HeroSearchFormSmall)/(experiences-search-form)/ExperiencesDateSingleInput.tsx
-
29src/app/(client-components)/(HeroSearchFormSmall)/(experiences-search-form)/ExperiencesSearchForm.tsx
-
148src/app/(client-components)/(HeroSearchFormSmall)/(flight-search-form)/FlightDateRangeInput.tsx
-
247src/app/(client-components)/(HeroSearchFormSmall)/(flight-search-form)/FlightSearchForm.tsx
-
51src/app/(experience-listings)/SectionGridFilterCard.tsx
-
113src/app/(experience-listings)/SectionGridHasMap.tsx
-
538src/app/(experience-listings)/TabFilters.tsx
-
56src/app/(experience-listings)/layout.tsx
-
14src/app/(experience-listings)/listing-experiences-map/page.tsx
-
14src/app/(experience-listings)/listing-experiences/page.tsx
-
195src/app/(home)/home-2/page.tsx
-
104src/app/(home)/home-3/page.tsx
-
55src/app/(listing-detail)/SectionDateRange.tsx
-
77src/app/(listing-detail)/layout.tsx
-
118src/app/(listing-detail)/listing-car-detail/RentalCarDatesRangeInput.tsx
-
68src/app/(listing-detail)/listing-car-detail/constant.ts
-
542src/app/(listing-detail)/listing-car-detail/page.tsx
-
122src/app/(listing-detail)/listing-experiences-detail/GuestsInput.tsx
-
109src/app/(listing-detail)/listing-experiences-detail/StayDatesRangeInput.tsx
-
39src/app/(listing-detail)/listing-experiences-detail/constant.ts
-
505src/app/(listing-detail)/listing-experiences-detail/page.tsx
-
122src/app/(listing-detail)/listing-stay-detail/GuestsInput.tsx
-
109src/app/(listing-detail)/listing-stay-detail/StayDatesRangeInput.tsx
-
71src/app/(listing-detail)/listing-stay-detail/constant.ts
-
637src/app/(listing-detail)/listing-stay-detail/page.tsx
-
4src/app/(real-estate-listings)/layout.tsx
-
45src/app/(server-components)/SectionHero2ArchivePage.tsx
-
281src/app/add-listing/[[...stepIndex]]/PageAddListing1.tsx
-
66src/app/add-listing/[[...stepIndex]]/page.tsx
-
2src/app/blog/SectionMagazine5.tsx
-
107src/app/custom-trip/page.tsx
-
89src/app/tours/[slug]/GuestsInput.tsx
-
8src/app/tours/[slug]/page.tsx
-
25src/app/tours/layout.tsx
-
75src/components/MobileFooterSticky.tsx
-
0src/components/ModalReserveMobile.tsx
-
28src/components/contexts/tourDetails.tsx
@ -0,0 +1,68 @@ |
|||
// BillCard.tsx
|
|||
import React from "react"; |
|||
import ButtonPrimary from "@/shared/ButtonPrimary"; |
|||
export type BillStatus = "Awaiting Payment" | "Approved" | "Rejected" | "Pending"; |
|||
import { BiSolidPlaneAlt } from "react-icons/bi"; |
|||
import { FaSimCard } from "react-icons/fa"; |
|||
import { BsCart3 } from "react-icons/bs"; |
|||
import { MdCurrencyExchange } from "react-icons/md"; |
|||
export interface Bill { |
|||
title: string; |
|||
issuedDate: string; |
|||
expirationDate: string; |
|||
amount: number; |
|||
status: BillStatus; |
|||
} |
|||
|
|||
|
|||
interface BillCardProps { |
|||
bill: Bill; |
|||
onViewDetail: (bill: Bill) => void; // Add onViewDetail prop
|
|||
} |
|||
|
|||
const statusStyles: { [key in BillStatus]: string } = { |
|||
"Awaiting Payment": "bg-yellow-200 text-yellow-700", |
|||
Approved: "bg-green-200 text-green-700", |
|||
Rejected: "bg-red-200 text-red-700", |
|||
Pending: "bg-blue-200 text-blue-700", |
|||
}; |
|||
|
|||
const BillCard: React.FC<BillCardProps> = ({ bill, onViewDetail }) => { |
|||
return ( |
|||
<div className="bg-white shadow-md rounded-lg p-4 mb-4"> |
|||
<div className="flex items-center justify-between"> |
|||
<h2 className="text-lg font-semibold flex items-center space-x-2"> |
|||
{bill.title.includes("Tour") && <span><BiSolidPlaneAlt className="text-bronze" size={25} /></span>} |
|||
{bill.title.includes("SIM Card") && <span><FaSimCard className="text-bronze" size={25} /></span>} |
|||
{bill.title.includes("Shop") && <span><BsCart3 className="text-bronze" size={25} /></span>} |
|||
{bill.title.includes("Tasrif") && <span><MdCurrencyExchange className="text-bronze" size={25} /></span>} |
|||
<span>{bill.title}</span> |
|||
</h2> |
|||
<span className={`px-2 py-1 text-sm rounded-full ${statusStyles[bill.status]}`}> |
|||
{bill.status} |
|||
</span> |
|||
</div> |
|||
|
|||
<div className="mt-4 text-sm text-gray-600"> |
|||
<div className="flex justify-between mb-2"> |
|||
<span>Issued Date:</span> |
|||
<span>{bill.issuedDate}</span> |
|||
</div> |
|||
<div className="flex justify-between mb-2"> |
|||
<span>Expiration Date:</span> |
|||
<span>{bill.expirationDate}</span> |
|||
</div> |
|||
<div className="flex justify-between mt-2 font-semibold"> |
|||
<span>Tour Invoice Amount:</span> |
|||
<span className="text-orange-600">${bill.amount.toFixed(2)}</span> |
|||
</div> |
|||
</div> |
|||
|
|||
<ButtonPrimary onClick={() => onViewDetail(bill)} className="mt-4"> |
|||
View Bill |
|||
</ButtonPrimary> |
|||
</div> |
|||
); |
|||
}; |
|||
|
|||
export default BillCard; |
@ -0,0 +1,113 @@ |
|||
// BillDetailCard.tsx
|
|||
import React from "react"; |
|||
import ButtonPrimary from "@/shared/ButtonPrimary"; |
|||
import Button from "@/shared/Button"; |
|||
|
|||
|
|||
export type BillStatus = "Awaiting Payment" | "Approved" | "Rejected" | "Pending"; |
|||
|
|||
export interface Bill { |
|||
title: string; |
|||
issuedDate: string; |
|||
expirationDate: string; |
|||
amount: number; |
|||
status: BillStatus; |
|||
passengers: { type: string; count: number }[]; |
|||
accountNumber: string; |
|||
message?: string; |
|||
uploadedImage?: { |
|||
name: string; |
|||
size: string; |
|||
src: string; |
|||
}; |
|||
} |
|||
|
|||
|
|||
|
|||
interface BillDetailCardProps { |
|||
bill: Bill; |
|||
} |
|||
|
|||
const statusStyles: { [key in BillStatus]: string } = { |
|||
"Awaiting Payment": "bg-yellow-200 text-yellow-700", |
|||
Approved: "bg-green-200 text-green-700", |
|||
Rejected: "bg-red-200 text-red-700", |
|||
Pending: "bg-blue-200 text-blue-700", |
|||
}; |
|||
|
|||
const BillDetailCard: React.FC<BillDetailCardProps> = ({ bill }) => { |
|||
return ( |
|||
<div className="bg-white shadow-md rounded-lg p-4 mb-4"> |
|||
<div className="flex items-center justify-between"> |
|||
<h2 className="text-lg font-semibold">{bill.title}</h2> |
|||
<span className={`px-2 py-1 text-sm rounded-full ${statusStyles[bill.status]}`}> |
|||
{bill.status} |
|||
</span> |
|||
</div> |
|||
|
|||
{bill.message && ( |
|||
<div className="bg-red-100 text-red-700 p-3 mt-4 rounded-md"> |
|||
<h3 className="font-semibold">Why was it rejected?</h3> |
|||
<p className="text-sm mt-1">{bill.message}</p> |
|||
</div> |
|||
)} |
|||
|
|||
<div className="mt-4 text-sm text-gray-600"> |
|||
<div className="flex justify-between mb-2"> |
|||
<span>Issued Date:</span> |
|||
<span>{bill.issuedDate}</span> |
|||
</div> |
|||
<div className="flex justify-between mb-2"> |
|||
<span>Expiration Date:</span> |
|||
<span>{bill.expirationDate}</span> |
|||
</div> |
|||
|
|||
<div className="mt-4"> |
|||
<h3 className="font-semibold">Number of Passengers</h3> |
|||
{bill.passengers.map((p, index) => ( |
|||
<div key={index} className="flex justify-between"> |
|||
<span>#{p.count} ({p.type})</span> |
|||
</div> |
|||
))} |
|||
</div> |
|||
|
|||
<div className="flex justify-between mt-4 font-semibold"> |
|||
<span>Tour Price:</span> |
|||
<span className="text-orange-600">${bill.amount}</span> |
|||
</div> |
|||
|
|||
<div className="mt-4"> |
|||
<h3 className="font-semibold">Account Number</h3> |
|||
<div className="flex items-center bg-gray-100 p-2 rounded-md mt-2"> |
|||
<span className="flex-grow">{bill.accountNumber}</span> |
|||
<Button className="text-blue-500">Copy</Button> |
|||
</div> |
|||
</div> |
|||
|
|||
{bill.uploadedImage ? ( |
|||
<div className="mt-4"> |
|||
<h3 className="font-semibold">Uploaded Image</h3> |
|||
<img src={bill.uploadedImage.src} alt="Uploaded" className="mt-2 rounded-md" /> |
|||
<div className="flex justify-between mt-2"> |
|||
<span>{bill.uploadedImage.name}</span> |
|||
<span className="text-gray-500 text-sm">{bill.uploadedImage.size}</span> |
|||
<Button className="text-red-500">Delete</Button> |
|||
</div> |
|||
</div> |
|||
) : ( |
|||
<div className="mt-4"> |
|||
<label className="block font-semibold mb-2">Upload Passport Image</label> |
|||
<div className="flex items-center bg-gray-100 p-2 rounded-md"> |
|||
<Button className="bg-orange-500 text-white">Upload</Button> |
|||
<span className="flex-grow text-gray-500 text-sm ml-4">No File Selected</span> |
|||
</div> |
|||
</div> |
|||
)} |
|||
</div> |
|||
|
|||
<ButtonPrimary className="w-full mt-6">Submit</ButtonPrimary> |
|||
</div> |
|||
); |
|||
}; |
|||
|
|||
export default BillDetailCard; |
@ -0,0 +1,106 @@ |
|||
// BillsPage.tsx
|
|||
"use client" |
|||
import React, { useState } from "react"; |
|||
import BillCard from "./BillCard"; |
|||
import BillDetailCard from "./[slug]/page"; |
|||
|
|||
|
|||
// types.ts
|
|||
export type BillStatus = "Awaiting Payment" | "Approved" | "Rejected" | "Pending"; |
|||
|
|||
export interface Bill { |
|||
title: string; |
|||
issuedDate: string; |
|||
expirationDate: string; |
|||
amount: number; |
|||
status: BillStatus; |
|||
passengers : {} |
|||
accountNumber: string, |
|||
message? : string |
|||
|
|||
} |
|||
|
|||
|
|||
const bills: Bill[] = [ |
|||
{ |
|||
title: "Karbala Tour Bill", |
|||
issuedDate: "12 Jan 2023", |
|||
expirationDate: "10 Jan 2023", |
|||
amount: 960, |
|||
status: "Awaiting Payment", |
|||
passengers: [ |
|||
{ type: "Adult", count: 3 }, |
|||
{ type: "Child", count: 2 }, |
|||
{ type: "Infant", count: 1 }, |
|||
], |
|||
accountNumber: "123-456-7890-0123", |
|||
}, |
|||
{ |
|||
title: "SIM Card Bill", |
|||
issuedDate: "12 Jan 2023", |
|||
expirationDate: "10 Jan 2023", |
|||
amount: 960, |
|||
status: "Approved", |
|||
passengers: [ |
|||
{ type: "Adult", count: 2 }, |
|||
{ type: "Child", count: 1 }, |
|||
], |
|||
accountNumber: "987-654-3210-0123", |
|||
}, |
|||
{ |
|||
title: "Shop Bill", |
|||
issuedDate: "12 Jan 2023", |
|||
expirationDate: "10 Jan 2023", |
|||
amount: 960, |
|||
status: "Rejected", |
|||
passengers: [ |
|||
{ type: "Adult", count: 1 }, |
|||
], |
|||
accountNumber: "456-789-0123-4567", |
|||
message: |
|||
"The uploaded image does not meet the required quality standards. Please use a higher-resolution photo.", |
|||
}, |
|||
{ |
|||
title: "Tasrif Bill", |
|||
issuedDate: "12 Jan 2023", |
|||
expirationDate: "10 Jan 2023", |
|||
amount: 960, |
|||
status: "Pending", |
|||
passengers: [ |
|||
{ type: "Adult", count: 3 }, |
|||
{ type: "Child", count: 2 }, |
|||
], |
|||
accountNumber: "321-654-9870-1234", |
|||
}, |
|||
]; |
|||
|
|||
const BillsPage: React.FC = () => { |
|||
const [selectedBill, setSelectedBill] = useState<Bill | null>(null); |
|||
|
|||
const handleViewDetail = (bill: Bill) => { |
|||
setSelectedBill(bill); |
|||
}; |
|||
|
|||
const handleBackToList = () => { |
|||
setSelectedBill(null); |
|||
}; |
|||
|
|||
return ( |
|||
<div className="p-4 bg-gray-100 min-h-screen"> |
|||
{selectedBill ? ( |
|||
<div> |
|||
<button onClick={handleBackToList} className="text-blue-500 mb-4"> |
|||
← Back to Bills |
|||
</button> |
|||
<BillDetailCard bill={selectedBill} /> |
|||
</div> |
|||
) : ( |
|||
bills.map((bill, index) => ( |
|||
<BillCard key={index} bill={bill} onViewDetail={handleViewDetail} /> |
|||
)) |
|||
)} |
|||
</div> |
|||
); |
|||
}; |
|||
|
|||
export default BillsPage; |
@ -1,48 +0,0 @@ |
|||
import React, { FC } from "react"; |
|||
import { DEMO_CAR_LISTINGS } from "@/data/listings"; |
|||
import { CarDataType } from "@/data/types"; |
|||
import Pagination from "@/shared/Pagination"; |
|||
import TabFilters from "./TabFilters"; |
|||
import Heading2 from "@/shared/Heading2"; |
|||
import CarCard from "@/components/CarCard"; |
|||
|
|||
export interface SectionGridFilterCardProps { |
|||
className?: string; |
|||
data?: CarDataType[]; |
|||
} |
|||
|
|||
const DEMO_DATA: CarDataType[] = DEMO_CAR_LISTINGS.filter((_, i) => i < 12); |
|||
|
|||
const SectionGridFilterCard: FC<SectionGridFilterCardProps> = ({ |
|||
className = "", |
|||
data = DEMO_DATA, |
|||
}) => { |
|||
return ( |
|||
<div className={`nc-SectionGridFilterCard ${className}`}> |
|||
<Heading2 |
|||
heading="Cars in Tokyo" |
|||
subHeading={ |
|||
<span className="block text-neutral-500 dark:text-neutral-400 mt-3"> |
|||
233 cars |
|||
<span className="mx-2">·</span> |
|||
Aug 12 - 18 |
|||
</span> |
|||
} |
|||
/> |
|||
|
|||
<div className="mb-8 lg:mb-11"> |
|||
<TabFilters /> |
|||
</div> |
|||
<div className="grid grid-cols-1 gap-6 md:gap-8 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4"> |
|||
{data.map((car) => ( |
|||
<CarCard key={car.id} data={car} /> |
|||
))} |
|||
</div> |
|||
<div className="flex mt-16 justify-center items-center"> |
|||
<Pagination /> |
|||
</div> |
|||
</div> |
|||
); |
|||
}; |
|||
|
|||
export default SectionGridFilterCard; |
@ -1,112 +0,0 @@ |
|||
"use client"; |
|||
|
|||
import React, { FC, useState } from "react"; |
|||
import GoogleMapReact from "google-map-react"; |
|||
import { DEMO_CAR_LISTINGS } from "@/data/listings"; |
|||
import ButtonClose from "@/shared/ButtonClose"; |
|||
import Checkbox from "@/shared/Checkbox"; |
|||
import Pagination from "@/shared/Pagination"; |
|||
import TabFilters from "./TabFilters"; |
|||
import Heading2 from "@/shared/Heading2"; |
|||
import CarCardH from "@/components/CarCardH"; |
|||
import AnyReactComponent from "@/components/AnyReactComponent/AnyReactComponent"; |
|||
|
|||
const DEMO_CARS = DEMO_CAR_LISTINGS.filter((_, i) => i < 12); |
|||
|
|||
export interface SectionGridHasMapProps {} |
|||
|
|||
const SectionGridHasMap: FC<SectionGridHasMapProps> = () => { |
|||
const [currentHoverID, setCurrentHoverID] = useState<string | number>(-1); |
|||
const [showFullMapFixed, setShowFullMapFixed] = useState(false); |
|||
|
|||
return ( |
|||
<div> |
|||
<div className="relative flex min-h-screen"> |
|||
{/* CARDSSSS */} |
|||
<div className="min-h-screen w-full xl:w-[780px] 2xl:w-[880px] flex-shrink-0 xl:px-8 "> |
|||
<Heading2 |
|||
heading="Cars in Tokyo" |
|||
subHeading={ |
|||
<span className="block text-neutral-500 dark:text-neutral-400 mt-3"> |
|||
233 cars |
|||
<span className="mx-2">·</span> |
|||
Aug 12 - 18 |
|||
</span> |
|||
} |
|||
/> |
|||
<div className="mb-8 lg:mb-11"> |
|||
<TabFilters /> |
|||
</div> |
|||
<div className="grid grid-cols-1 gap-8"> |
|||
{DEMO_CARS.map((item) => ( |
|||
<div |
|||
key={item.id} |
|||
onMouseEnter={() => setCurrentHoverID((_) => item.id)} |
|||
onMouseLeave={() => setCurrentHoverID((_) => -1)} |
|||
> |
|||
<CarCardH data={item} /> |
|||
</div> |
|||
))} |
|||
</div> |
|||
<div className="flex mt-16 justify-center items-center"> |
|||
<Pagination /> |
|||
</div> |
|||
</div> |
|||
|
|||
<div |
|||
className="flex xl:hidden items-center justify-center fixed bottom-8 left-1/2 transform -translate-x-1/2 px-6 py-2 bg-neutral-900 text-white shadow-2xl rounded-full z-30 space-x-3 text-sm cursor-pointer" |
|||
onClick={() => setShowFullMapFixed(true)} |
|||
> |
|||
<i className="text-lg las la-map"></i> |
|||
<span>Show map</span> |
|||
</div> |
|||
|
|||
{/* MAPPPPP */} |
|||
<div |
|||
className={`xl:flex-grow xl:static xl:block ${ |
|||
showFullMapFixed ? "fixed inset-0 z-50" : "hidden" |
|||
}`}
|
|||
> |
|||
{showFullMapFixed && ( |
|||
<ButtonClose |
|||
onClick={() => setShowFullMapFixed(false)} |
|||
className="bg-white absolute z-50 left-3 top-3 shadow-lg rounded-xl w-10 h-10" |
|||
/> |
|||
)} |
|||
|
|||
<div className="fixed xl:sticky top-0 xl:top-[88px] left-0 w-full h-full xl:h-[calc(100vh-88px)] rounded-md overflow-hidden"> |
|||
<div className="absolute bottom-5 left-3 lg:bottom-auto lg:top-2.5 lg:left-1/2 transform lg:-translate-x-1/2 py-2 px-4 bg-white shadow-xl z-10 rounded-2xl min-w-max"> |
|||
<Checkbox |
|||
className="text-xs xl:text-sm text-neutral-800" |
|||
name="xx" |
|||
label="Search ass I move the map" |
|||
/> |
|||
</div> |
|||
{/* BELLOW IS MY GOOGLE API KEY -- PLEASE DELETE AND TYPE YOUR API KEY */} |
|||
|
|||
<GoogleMapReact |
|||
bootstrapURLKeys={{ |
|||
key: "AIzaSyAGVJfZMAKYfZ71nzL_v5i3LjTTWnCYwTY", |
|||
}} |
|||
yesIWantToUseGoogleMapApiInternals |
|||
defaultZoom={12} |
|||
defaultCenter={DEMO_CARS[0].map} |
|||
> |
|||
{DEMO_CARS.map((item) => ( |
|||
<AnyReactComponent |
|||
isSelected={currentHoverID === item.id} |
|||
key={item.id} |
|||
lat={item.map.lat} |
|||
lng={item.map.lng} |
|||
car={item} |
|||
/> |
|||
))} |
|||
</GoogleMapReact> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
); |
|||
}; |
|||
|
|||
export default SectionGridHasMap; |
@ -1,683 +0,0 @@ |
|||
"use client"; |
|||
|
|||
import React, { Fragment, 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 convertNumbThousand from "@/utils/convertNumbThousand"; |
|||
import Slider from "rc-slider"; |
|||
|
|||
// DEMO DATA
|
|||
const typeOfCar = [ |
|||
{ name: "Small", description: "$68" }, |
|||
{ name: "Medium", description: "$128" }, |
|||
{ name: "Large", description: "$268" }, |
|||
{ name: "SUV", description: "$268" }, |
|||
{ name: "Van", description: "$268" }, |
|||
{ name: "Luxury", description: "$268" }, |
|||
]; |
|||
|
|||
const carSpecifications = [ |
|||
{ name: "With air conditioning" }, |
|||
{ name: "Automatic transmission" }, |
|||
{ name: "Manual transmission" }, |
|||
{ name: "2 doors" }, |
|||
{ name: "4+ doors" }, |
|||
]; |
|||
|
|||
//
|
|||
const mileage = [{ name: "Unlimited" }, { name: "Limited" }]; |
|||
const supplier = [ |
|||
{ name: "Alamo", defaultChecked: true }, |
|||
{ name: "Avis", defaultChecked: true }, |
|||
{ name: "Budget" }, |
|||
{ name: "Dollar" }, |
|||
]; |
|||
const insurance = [ |
|||
{ name: "No insurance", defaultChecked: true }, |
|||
{ name: "Zero excess " }, |
|||
{ name: "Inclusive" }, |
|||
]; |
|||
|
|||
const TabFilters = () => { |
|||
const [isOpenMoreFilter, setisOpenMoreFilter] = useState(false); |
|||
const [isOpenMoreFilterMobile, setisOpenMoreFilterMobile] = useState(false); |
|||
const [rangePrices, setRangePrices] = useState([0, 1000]); |
|||
const [isOnSale, setIsOnSale] = useState(true); |
|||
//
|
|||
const closeModalMoreFilter = () => setisOpenMoreFilter(false); |
|||
const openModalMoreFilter = () => setisOpenMoreFilter(true); |
|||
//
|
|||
const closeModalMoreFilterMobile = () => setisOpenMoreFilterMobile(false); |
|||
const openModalMoreFilterMobile = () => setisOpenMoreFilterMobile(true); |
|||
|
|||
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 renderTabsTypeOfCars = () => { |
|||
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>Car type</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"> |
|||
{typeOfCar.map((item) => ( |
|||
<div key={item.name} className=""> |
|||
<Checkbox |
|||
name={item.name} |
|||
label={item.name} |
|||
subLabel={item.description} |
|||
/> |
|||
</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> |
|||
); |
|||
}; |
|||
|
|||
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> |
|||
); |
|||
}; |
|||
|
|||
const renderTabOnSale = () => { |
|||
return ( |
|||
<div |
|||
className={`flex items-center justify-center px-4 py-2 text-sm rounded-full border focus:outline-none cursor-pointer transition-all ${ |
|||
isOnSale |
|||
? "border-primary-500 bg-primary-50 text-primary-700" |
|||
: "border-neutral-300 dark:border-neutral-700" |
|||
}`}
|
|||
onClick={() => setIsOnSale(!isOnSale)} |
|||
> |
|||
<span>On sale</span> |
|||
{isOnSale && renderXClear()} |
|||
</div> |
|||
); |
|||
}; |
|||
|
|||
const renderTabsGuestsAndBags = () => { |
|||
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>Guests & Bags</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="Passengers" max={40} /> |
|||
<NcInputNumber label="Bags" max={40} /> |
|||
</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 renderMoreFilterItem = ( |
|||
data: { |
|||
name: string; |
|||
description?: 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-y-7 gap-x-4 sm:gap-8"> |
|||
<div className="flex flex-col space-y-5"> |
|||
{list1.map((item) => ( |
|||
<Checkbox |
|||
key={item.name} |
|||
name={item.name} |
|||
subLabel={item.description} |
|||
label={item.name} |
|||
defaultChecked={!!item.defaultChecked} |
|||
/> |
|||
))} |
|||
</div> |
|||
<div className="flex flex-col space-y-5"> |
|||
{list2.map((item) => ( |
|||
<Checkbox |
|||
key={item.name} |
|||
name={item.name} |
|||
subLabel={item.description} |
|||
label={item.name} |
|||
defaultChecked={!!item.defaultChecked} |
|||
/> |
|||
))} |
|||
</div> |
|||
</div> |
|||
); |
|||
}; |
|||
|
|||
// Morefilter for mobile mode
|
|||
const renderTabMobileFilter = () => { |
|||
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={openModalMoreFilterMobile} |
|||
> |
|||
<span> |
|||
<span className="hidden sm:inline">Car</span> filters (3) |
|||
</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" |
|||
> |
|||
Experiences 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 car</h3> |
|||
<div className="mt-6 relative "> |
|||
{renderMoreFilterItem(typeOfCar)} |
|||
</div> |
|||
</div> |
|||
|
|||
{/* ------ */} |
|||
<div className="py-7"> |
|||
<h3 className="text-xl font-medium"> |
|||
Car specifications |
|||
</h3> |
|||
<div className="mt-6 relative "> |
|||
{renderMoreFilterItem(carSpecifications)} |
|||
</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"> |
|||
Passengers & Bags |
|||
</h3> |
|||
<div className="mt-6 relative flex-col px-5 py-6 space-y-5"> |
|||
<NcInputNumber label="Passengers" max={40} /> |
|||
<NcInputNumber label="Bags" max={40} /> |
|||
</div> |
|||
</div> |
|||
|
|||
{/* ------ */} |
|||
<div className="py-7"> |
|||
<h3 className="text-xl font-medium">Mileage</h3> |
|||
<div className="mt-6 relative "> |
|||
{renderMoreFilterItem(mileage)} |
|||
</div> |
|||
</div> |
|||
|
|||
{/* ------ */} |
|||
<div className="py-7"> |
|||
<h3 className="text-xl font-medium">Supplier</h3> |
|||
<div className="mt-6 relative "> |
|||
{renderMoreFilterItem(supplier)} |
|||
</div> |
|||
</div> |
|||
|
|||
{/* ------ */} |
|||
<div className="py-7"> |
|||
<h3 className="text-xl font-medium">Insurance</h3> |
|||
<div className="mt-6 relative "> |
|||
{renderMoreFilterItem(insurance)} |
|||
</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> |
|||
); |
|||
}; |
|||
|
|||
//
|
|||
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-4 sm:px-6 divide-y divide-neutral-200 dark:divide-neutral-800"> |
|||
<div className="py-7"> |
|||
<h3 className="text-xl font-medium"> |
|||
Car specifications |
|||
</h3> |
|||
<div className="mt-6 relative "> |
|||
{renderMoreFilterItem(carSpecifications)} |
|||
</div> |
|||
</div> |
|||
<div className="py-7"> |
|||
<h3 className="text-xl font-medium">Mileage</h3> |
|||
<div className="mt-6 relative "> |
|||
{renderMoreFilterItem(mileage)} |
|||
</div> |
|||
</div> |
|||
<div className="py-7"> |
|||
<h3 className="text-xl font-medium">Supplier</h3> |
|||
<div className="mt-6 relative "> |
|||
{renderMoreFilterItem(supplier)} |
|||
</div> |
|||
</div> |
|||
<div className="py-7"> |
|||
<h3 className="text-xl font-medium">Insurance</h3> |
|||
<div className="mt-6 relative "> |
|||
{renderMoreFilterItem(insurance)} |
|||
</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> |
|||
); |
|||
}; |
|||
|
|||
return ( |
|||
<div className="flex lg:space-x-4"> |
|||
<div className="hidden lg:flex space-x-4"> |
|||
{renderTabsTypeOfCars()} |
|||
{renderTabsPriceRage()} |
|||
{renderTabsGuestsAndBags()} |
|||
{renderTabMoreFilter()} |
|||
</div> |
|||
<div className="flex lg:hidden space-x-4"> |
|||
{renderTabMobileFilter()} |
|||
{renderTabOnSale()} |
|||
</div> |
|||
</div> |
|||
); |
|||
}; |
|||
|
|||
export default TabFilters; |
@ -1,59 +0,0 @@ |
|||
import BgGlassmorphism from "@/components/BgGlassmorphism"; |
|||
import React, { ReactNode } from "react"; |
|||
import SectionHeroArchivePage from "../(server-components)/SectionHeroArchivePage"; |
|||
import heroRightImage from "@/images/hero-right-car.png"; |
|||
import BackgroundSection from "@/components/BackgroundSection"; |
|||
import SectionSliderNewCategories from "@/components/SectionSliderNewCategories"; |
|||
import SectionSubscribe2 from "@/components/SectionSubscribe2"; |
|||
import SectionGridAuthorBox from "@/components/SectionGridAuthorBox"; |
|||
|
|||
const Layout = ({ children }: { children: ReactNode }) => { |
|||
return ( |
|||
<div className={`nc-ListingCarMapPage relative `}> |
|||
<BgGlassmorphism /> |
|||
|
|||
{/* SECTION HERO */} |
|||
<div className="container pt-10 pb-24 lg:pt-16 lg:pb-28"> |
|||
<SectionHeroArchivePage |
|||
rightImage={heroRightImage} |
|||
currentPage="Cars" |
|||
currentTab="Cars" |
|||
listingType={ |
|||
<> |
|||
<i className="text-2xl las la-car"></i> |
|||
<span className="ml-2.5">1512 cars</span> |
|||
</> |
|||
} |
|||
/> |
|||
</div> |
|||
|
|||
{/* SECTION */} |
|||
{children} |
|||
|
|||
<div className="container overflow-hidden"> |
|||
{/* SECTION 1 */} |
|||
<div className="relative py-16"> |
|||
<BackgroundSection /> |
|||
<SectionSliderNewCategories |
|||
heading="Explore by types of stays" |
|||
subHeading="Explore houses based on 10 types of stays" |
|||
categoryCardType="card5" |
|||
itemPerRow={5} |
|||
sliderStyle="style2" |
|||
/> |
|||
</div> |
|||
|
|||
{/* SECTION */} |
|||
<SectionSubscribe2 className="py-24 lg:py-28" /> |
|||
|
|||
{/* SECTION */} |
|||
<div className="relative py-16 mb-24 lg:mb-28"> |
|||
<BackgroundSection className="bg-orange-50 dark:bg-black dark:bg-opacity-20 " /> |
|||
<SectionGridAuthorBox /> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
); |
|||
}; |
|||
|
|||
export default Layout; |
@ -1,14 +0,0 @@ |
|||
import React, { FC } from "react"; |
|||
import SectionGridHasMap from "../SectionGridHasMap"; |
|||
|
|||
export interface ListingCarMapPageProps {} |
|||
|
|||
const ListingCarMapPage: FC<ListingCarMapPageProps> = () => { |
|||
return ( |
|||
<div className="container pb-24 lg:pb-28 2xl:pl-10 xl:pr-0 xl:max-w-none"> |
|||
<SectionGridHasMap /> |
|||
</div> |
|||
); |
|||
}; |
|||
|
|||
export default ListingCarMapPage; |
@ -1,14 +0,0 @@ |
|||
import React, { FC } from "react"; |
|||
import SectionGridFilterCard from "../SectionGridFilterCard"; |
|||
|
|||
export interface ListingCarPageProps {} |
|||
|
|||
const ListingCarPage: FC<ListingCarPageProps> = () => { |
|||
return ( |
|||
<div className="container "> |
|||
<SectionGridFilterCard className="pb-24 lg:pb-28" /> |
|||
</div> |
|||
); |
|||
}; |
|||
|
|||
export default ListingCarPage; |
@ -1,134 +0,0 @@ |
|||
"use client"; |
|||
|
|||
import React, { Fragment, useState } from "react"; |
|||
import { FC } from "react"; |
|||
import DatePicker from "react-datepicker"; |
|||
import { Popover, Transition } from "@headlessui/react"; |
|||
import { CalendarIcon } from "@heroicons/react/24/outline"; |
|||
import DatePickerCustomHeaderTwoMonth from "@/components/DatePickerCustomHeaderTwoMonth"; |
|||
import DatePickerCustomDay from "@/components/DatePickerCustomDay"; |
|||
import ClearDataButton from "../ClearDataButton"; |
|||
import ButtonSubmit from "../ButtonSubmit"; |
|||
|
|||
export interface RentalCarDatesRangeInputProps { |
|||
className?: string; |
|||
fieldClassName?: string; |
|||
hasButtonSubmit?: boolean; |
|||
} |
|||
|
|||
const RentalCarDatesRangeInput: FC<RentalCarDatesRangeInputProps> = ({ |
|||
className = "", |
|||
fieldClassName = "[ nc-hero-field-padding ]", |
|||
hasButtonSubmit = true, |
|||
}) => { |
|||
const [startDate, setStartDate] = useState<Date | null>( |
|||
new Date("2023/03/01") |
|||
); |
|||
const [endDate, setEndDate] = useState<Date | null>(new Date("2023/03/16")); |
|||
|
|||
const onChangeDate = (dates: [Date | null, Date | null]) => { |
|||
const [start, end] = dates; |
|||
setStartDate(start); |
|||
setEndDate(end); |
|||
}; |
|||
|
|||
const renderInput = () => { |
|||
return ( |
|||
<> |
|||
<div className="text-neutral-300 dark:text-neutral-400"> |
|||
<CalendarIcon className="w-5 h-5 lg:w-7 lg:h-7" /> |
|||
</div> |
|||
<div className="flex-grow text-left"> |
|||
<span className="block xl:text-lg font-semibold"> |
|||
{startDate?.toLocaleDateString("en-US", { |
|||
month: "short", |
|||
day: "2-digit", |
|||
}) || "Add dates"} |
|||
{endDate |
|||
? " - " + |
|||
endDate?.toLocaleDateString("en-US", { |
|||
month: "short", |
|||
day: "2-digit", |
|||
}) |
|||
: ""} |
|||
</span> |
|||
<span className="block mt-1 text-sm text-neutral-400 leading-none font-light"> |
|||
{"Pick up - Drop off"} |
|||
</span> |
|||
</div> |
|||
</> |
|||
); |
|||
}; |
|||
|
|||
return ( |
|||
<> |
|||
<Popover |
|||
className={`RentalCarDatesRangeInput relative flex ${className}`} |
|||
> |
|||
{({ open }) => ( |
|||
<> |
|||
<div |
|||
className={`flex-1 z-10 flex items-center focus:outline-none ${ |
|||
open ? "nc-hero-field-focused" : "" |
|||
}`}
|
|||
> |
|||
<Popover.Button |
|||
className={`flex-1 z-10 flex relative ${fieldClassName} items-center space-x-3 focus:outline-none `} |
|||
> |
|||
{renderInput()} |
|||
|
|||
{startDate && open && ( |
|||
<ClearDataButton onClick={() => onChangeDate([null, null])} /> |
|||
)} |
|||
</Popover.Button> |
|||
|
|||
{/* BUTTON SUBMIT OF FORM */} |
|||
{hasButtonSubmit && ( |
|||
<div className="pr-2 xl:pr-4"> |
|||
<ButtonSubmit href="/listing-car-detail" /> |
|||
</div> |
|||
)} |
|||
</div> |
|||
|
|||
{open && ( |
|||
<div className="h-8 absolute self-center top-1/2 -translate-y-1/2 z-0 -left-0.5 right-0.5 bg-white dark:bg-neutral-800"></div> |
|||
)} |
|||
|
|||
<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 left-1/2 z-10 mt-3 top-full w-screen max-w-sm -translate-x-1/2 transform px-4 sm:px-0 lg:max-w-3xl"> |
|||
<div className="overflow-hidden rounded-3xl shadow-lg ring-1 ring-black ring-opacity-5 bg-white dark:bg-neutral-800 p-8"> |
|||
<DatePicker |
|||
selected={startDate} |
|||
onChange={onChangeDate} |
|||
startDate={startDate} |
|||
endDate={endDate} |
|||
selectsRange |
|||
monthsShown={2} |
|||
showPopperArrow={false} |
|||
inline |
|||
renderCustomHeader={(p) => ( |
|||
<DatePickerCustomHeaderTwoMonth {...p} /> |
|||
)} |
|||
renderDayContents={(day, date) => ( |
|||
<DatePickerCustomDay dayOfMonth={day} date={date} /> |
|||
)} |
|||
/> |
|||
</div> |
|||
</Popover.Panel> |
|||
</Transition> |
|||
</> |
|||
)} |
|||
</Popover> |
|||
</> |
|||
); |
|||
}; |
|||
|
|||
export default RentalCarDatesRangeInput; |
@ -1,70 +0,0 @@ |
|||
"use client"; |
|||
|
|||
import React, { FC, useState } from "react"; |
|||
import LocationInput from "../LocationInput"; |
|||
import RentalCarDatesRangeInput from "./RentalCarDatesRangeInput"; |
|||
|
|||
export interface RentalCarSearchFormProps {} |
|||
|
|||
const RentalCarSearchForm: FC<RentalCarSearchFormProps> = ({}) => { |
|||
const [dropOffLocationType, setDropOffLocationType] = useState< |
|||
"same" | "different" |
|||
>("different"); |
|||
|
|||
const renderRadioBtn = () => { |
|||
return ( |
|||
<div className=" py-5 [ nc-hero-field-padding ] flex items-center flex-wrap flex-row border-b border-neutral-100 dark:border-neutral-700"> |
|||
<div |
|||
className={`py-1.5 px-4 flex items-center rounded-full font-medium text-xs cursor-pointer mr-2 my-1 sm:mr-3 ${ |
|||
dropOffLocationType === "different" |
|||
? "bg-black text-white shadow-black/10 shadow-lg" |
|||
: "border border-neutral-300 dark:border-neutral-700" |
|||
}`}
|
|||
onClick={(e) => setDropOffLocationType("different")} |
|||
> |
|||
Different drop off |
|||
</div> |
|||
<div |
|||
className={`py-1.5 px-4 flex items-center rounded-full font-medium text-xs cursor-pointer mr-2 my-1 sm:mr-3 ${ |
|||
dropOffLocationType === "same" |
|||
? "bg-black text-white shadow-black/10 shadow-lg" |
|||
: "border border-neutral-300 dark:border-neutral-700" |
|||
}`}
|
|||
onClick={(e) => setDropOffLocationType("same")} |
|||
> |
|||
Same drop off |
|||
</div> |
|||
</div> |
|||
); |
|||
}; |
|||
|
|||
const isDdropOffdifferent = dropOffLocationType === "different"; |
|||
|
|||
return ( |
|||
<form className="w-full relative mt-8 rounded-[40px] xl:rounded-[49px] rounded-t-2xl xl:rounded-t-3xl shadow-xl dark:shadow-2xl bg-white dark:bg-neutral-800"> |
|||
{renderRadioBtn()} |
|||
<div className={`relative flex flex-row`}> |
|||
<LocationInput |
|||
placeHolder="City or Airport" |
|||
desc="Pick up location" |
|||
className="flex-1" |
|||
/> |
|||
{isDdropOffdifferent && ( |
|||
<> |
|||
<div className="self-center border-r border-slate-200 dark:border-slate-700 h-8"></div> |
|||
<LocationInput |
|||
placeHolder="City or Airport" |
|||
desc="Drop off location" |
|||
className="flex-1" |
|||
divHideVerticalLineClass="-inset-x-0.5" |
|||
/> |
|||
</> |
|||
)} |
|||
<div className="self-center border-r border-slate-200 dark:border-slate-700 h-8"></div> |
|||
<RentalCarDatesRangeInput className="flex-1" /> |
|||
</div> |
|||
</form> |
|||
); |
|||
}; |
|||
|
|||
export default RentalCarSearchForm; |
@ -1,119 +0,0 @@ |
|||
"use client"; |
|||
|
|||
import React, { Fragment, useState } from "react"; |
|||
import { FC } from "react"; |
|||
import DatePicker from "react-datepicker"; |
|||
import { Popover, Transition } from "@headlessui/react"; |
|||
import { CalendarIcon } from "@heroicons/react/24/outline"; |
|||
import DatePickerCustomHeaderTwoMonth from "@/components/DatePickerCustomHeaderTwoMonth"; |
|||
import DatePickerCustomDay from "@/components/DatePickerCustomDay"; |
|||
import ClearDataButton from "../ClearDataButton"; |
|||
|
|||
export interface ExperiencesDateSingleInputProps { |
|||
className?: string; |
|||
fieldClassName?: string; |
|||
} |
|||
|
|||
const ExperiencesDateSingleInput: FC<ExperiencesDateSingleInputProps> = ({ |
|||
className = "", |
|||
fieldClassName = "[ nc-hero-field-padding ]", |
|||
}) => { |
|||
const [startDate, setStartDate] = useState<Date | null>( |
|||
new Date("2023/03/01") |
|||
); |
|||
const [endDate, setEndDate] = useState<Date | null>(new Date("2023/03/16")); |
|||
|
|||
const onChangeDate = (dates: [Date | null, Date | null]) => { |
|||
const [start, end] = dates; |
|||
setStartDate(start); |
|||
setEndDate(end); |
|||
}; |
|||
|
|||
const renderInput = () => { |
|||
return ( |
|||
<> |
|||
<div className="text-neutral-300 dark:text-neutral-400"> |
|||
<CalendarIcon className="w-5 h-5 lg:w-7 lg:h-7" /> |
|||
</div> |
|||
<div className="flex-grow text-left"> |
|||
<span className="block xl:text-lg font-semibold"> |
|||
{startDate?.toLocaleDateString("en-US", { |
|||
month: "short", |
|||
day: "2-digit", |
|||
}) || "Date"} |
|||
{endDate |
|||
? " - " + |
|||
endDate?.toLocaleDateString("en-US", { |
|||
month: "short", |
|||
day: "2-digit", |
|||
}) |
|||
: ""} |
|||
</span> |
|||
<span className="block mt-1 text-sm text-neutral-400 leading-none font-light"> |
|||
{startDate ? "Date" : `Add dates`} |
|||
</span> |
|||
</div> |
|||
</> |
|||
); |
|||
}; |
|||
|
|||
return ( |
|||
<> |
|||
<Popover |
|||
className={`ExperiencesDateSingleInput relative flex ${className}`} |
|||
> |
|||
{({ open }) => ( |
|||
<> |
|||
<Popover.Button |
|||
className={`flex-1 z-10 flex relative ${fieldClassName} items-center space-x-3 focus:outline-none ${ |
|||
open ? "nc-hero-field-focused" : "" |
|||
}`}
|
|||
> |
|||
{renderInput()} |
|||
{startDate && open && ( |
|||
<ClearDataButton onClick={() => onChangeDate([null, null])} /> |
|||
)} |
|||
</Popover.Button> |
|||
|
|||
{open && ( |
|||
<div className="h-8 absolute self-center top-1/2 -translate-y-1/2 z-0 -inset-x-0.5 bg-white dark:bg-neutral-800"></div> |
|||
)} |
|||
|
|||
<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 left-1/2 z-10 mt-3 top-full w-screen max-w-sm -translate-x-1/2 transform px-4 sm:px-0 lg:max-w-3xl"> |
|||
<div className="overflow-hidden rounded-3xl shadow-lg ring-1 ring-black ring-opacity-5 bg-white dark:bg-neutral-800 p-8"> |
|||
<DatePicker |
|||
selected={startDate} |
|||
onChange={onChangeDate} |
|||
startDate={startDate} |
|||
endDate={endDate} |
|||
selectsRange |
|||
monthsShown={2} |
|||
showPopperArrow={false} |
|||
inline |
|||
renderCustomHeader={(p) => ( |
|||
<DatePickerCustomHeaderTwoMonth {...p} /> |
|||
)} |
|||
renderDayContents={(day, date) => ( |
|||
<DatePickerCustomDay dayOfMonth={day} date={date} /> |
|||
)} |
|||
/> |
|||
</div> |
|||
</Popover.Panel> |
|||
</Transition> |
|||
</> |
|||
)} |
|||
</Popover> |
|||
</> |
|||
); |
|||
}; |
|||
|
|||
export default ExperiencesDateSingleInput; |
@ -1,27 +0,0 @@ |
|||
import React, { FC } from "react"; |
|||
import LocationInput from "../LocationInput"; |
|||
import GuestsInput from "../GuestsInput"; |
|||
import ExperiencesDateSingleInput from "./ExperiencesDateSingleInput"; |
|||
|
|||
export interface ExperiencesSearchFormProps {} |
|||
|
|||
const ExperiencesSearchForm: FC<ExperiencesSearchFormProps> = ({}) => { |
|||
const renderForm = () => { |
|||
return ( |
|||
<form className="w-full relative mt-8 flex flex-col md:flex-row rounded-3xl md:rounded-full shadow-xl dark:shadow-2xl bg-white dark:bg-neutral-800 "> |
|||
<LocationInput className="flex-[1.5]" /> |
|||
<div className="self-center border-r border-slate-200 dark:border-slate-700 h-8"></div> |
|||
<ExperiencesDateSingleInput className="flex-1" /> |
|||
<div className="self-center border-r border-slate-200 dark:border-slate-700 h-8"></div> |
|||
<GuestsInput |
|||
className="flex-1" |
|||
buttonSubmitHref="/listing-experiences" |
|||
/> |
|||
</form> |
|||
); |
|||
}; |
|||
|
|||
return renderForm(); |
|||
}; |
|||
|
|||
export default ExperiencesSearchForm; |
@ -1,152 +0,0 @@ |
|||
"use client"; |
|||
|
|||
import React, { Fragment, useState } from "react"; |
|||
import { FC } from "react"; |
|||
import DatePicker from "react-datepicker"; |
|||
import { Popover, Transition } from "@headlessui/react"; |
|||
import { CalendarIcon } from "@heroicons/react/24/outline"; |
|||
import DatePickerCustomHeaderTwoMonth from "@/components/DatePickerCustomHeaderTwoMonth"; |
|||
import DatePickerCustomDay from "@/components/DatePickerCustomDay"; |
|||
import ClearDataButton from "../ClearDataButton"; |
|||
import ButtonSubmit from "../ButtonSubmit"; |
|||
|
|||
export interface FlightDateRangeInputProps { |
|||
className?: string; |
|||
fieldClassName?: string; |
|||
hasButtonSubmit?: boolean; |
|||
selectsRange?: boolean; |
|||
} |
|||
|
|||
const FlightDateRangeInput: FC<FlightDateRangeInputProps> = ({ |
|||
className = "", |
|||
fieldClassName = "[ nc-hero-field-padding ]", |
|||
hasButtonSubmit = true, |
|||
selectsRange = true, |
|||
}) => { |
|||
const [startDate, setStartDate] = useState<Date | null>( |
|||
new Date("2023/05/01") |
|||
); |
|||
const [endDate, setEndDate] = useState<Date | null>(new Date("2023/05/16")); |
|||
|
|||
const onChangeRangeDate = (dates: [Date | null, Date | null]) => { |
|||
const [start, end] = dates; |
|||
setStartDate(start); |
|||
setEndDate(end); |
|||
}; |
|||
|
|||
const renderInput = () => { |
|||
return ( |
|||
<> |
|||
<div className="text-neutral-300 dark:text-neutral-400"> |
|||
<CalendarIcon className="w-5 h-5 lg:w-7 lg:h-7" /> |
|||
</div> |
|||
<div className="flex-grow text-left"> |
|||
<span className="block xl:text-lg font-semibold"> |
|||
{startDate?.toLocaleDateString("en-US", { |
|||
month: "short", |
|||
day: "2-digit", |
|||
}) || "Add dates"} |
|||
{selectsRange && endDate |
|||
? " - " + |
|||
endDate?.toLocaleDateString("en-US", { |
|||
month: "short", |
|||
day: "2-digit", |
|||
}) |
|||
: ""} |
|||
</span> |
|||
<span className="block mt-1 text-sm text-neutral-400 leading-none font-light"> |
|||
{selectsRange ? "Pick up - Drop off" : "Pick up date"} |
|||
</span> |
|||
</div> |
|||
</> |
|||
); |
|||
}; |
|||
|
|||
return ( |
|||
<> |
|||
<Popover className={`FlightDateRangeInput relative flex ${className}`}> |
|||
{({ open }) => ( |
|||
<> |
|||
<div |
|||
className={`flex-1 z-10 flex items-center focus:outline-none ${ |
|||
open ? "nc-hero-field-focused" : "" |
|||
}`}
|
|||
> |
|||
<Popover.Button |
|||
className={`flex-1 z-10 flex relative ${fieldClassName} items-center space-x-3 focus:outline-none `} |
|||
> |
|||
{renderInput()} |
|||
|
|||
{startDate && open && ( |
|||
<ClearDataButton |
|||
onClick={() => onChangeRangeDate([null, null])} |
|||
/> |
|||
)} |
|||
</Popover.Button> |
|||
|
|||
{/* BUTTON SUBMIT OF FORM */} |
|||
{hasButtonSubmit && ( |
|||
<div className="pr-2 xl:pr-4"> |
|||
<ButtonSubmit href="/listing-car-detail" /> |
|||
</div> |
|||
)} |
|||
</div> |
|||
|
|||
{open && ( |
|||
<div className="h-8 absolute self-center top-1/2 -translate-y-1/2 z-0 -left-0.5 right-10 bg-white dark:bg-neutral-800"></div> |
|||
)} |
|||
|
|||
<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 left-1/2 z-20 mt-3 top-full w-screen max-w-sm -translate-x-1/2 transform px-4 sm:px-0 lg:max-w-3xl"> |
|||
<div className="overflow-hidden rounded-3xl shadow-lg ring-1 ring-black ring-opacity-5 bg-white dark:bg-neutral-800 p-8"> |
|||
{selectsRange ? ( |
|||
<DatePicker |
|||
selected={startDate} |
|||
onChange={onChangeRangeDate} |
|||
startDate={startDate} |
|||
endDate={endDate} |
|||
selectsRange |
|||
monthsShown={2} |
|||
showPopperArrow={false} |
|||
inline |
|||
renderCustomHeader={(p) => ( |
|||
<DatePickerCustomHeaderTwoMonth {...p} /> |
|||
)} |
|||
renderDayContents={(day, date) => ( |
|||
<DatePickerCustomDay dayOfMonth={day} date={date} /> |
|||
)} |
|||
/> |
|||
) : ( |
|||
<DatePicker |
|||
selected={startDate} |
|||
onChange={(date) => setStartDate(date)} |
|||
monthsShown={2} |
|||
showPopperArrow={false} |
|||
inline |
|||
renderCustomHeader={(p) => ( |
|||
<DatePickerCustomHeaderTwoMonth {...p} /> |
|||
)} |
|||
renderDayContents={(day, date) => ( |
|||
<DatePickerCustomDay dayOfMonth={day} date={date} /> |
|||
)} |
|||
/> |
|||
)} |
|||
</div> |
|||
</Popover.Panel> |
|||
</Transition> |
|||
</> |
|||
)} |
|||
</Popover> |
|||
</> |
|||
); |
|||
}; |
|||
|
|||
export default FlightDateRangeInput; |
@ -1,246 +0,0 @@ |
|||
"use client"; |
|||
|
|||
import React, { FC, useState } from "react"; |
|||
import LocationInput from "../LocationInput"; |
|||
import { Popover, Transition } from "@headlessui/react"; |
|||
import { ChevronDownIcon } from "@heroicons/react/24/solid"; |
|||
import { Fragment } from "react"; |
|||
import NcInputNumber from "@/components/NcInputNumber"; |
|||
import FlightDateRangeInput from "./FlightDateRangeInput"; |
|||
import { GuestsObject } from "../../type"; |
|||
|
|||
export interface FlightSearchFormProps {} |
|||
|
|||
const flightClass = [ |
|||
{ |
|||
name: "Economy", |
|||
href: "##", |
|||
}, |
|||
{ |
|||
name: "Business", |
|||
href: "##", |
|||
}, |
|||
{ |
|||
name: "Multiple", |
|||
href: "##", |
|||
}, |
|||
]; |
|||
|
|||
export type TypeDropOffLocationType = "roundTrip" | "oneWay" | ""; |
|||
|
|||
const FlightSearchForm: FC<FlightSearchFormProps> = ({}) => { |
|||
const [dropOffLocationType, setDropOffLocationType] = |
|||
useState<TypeDropOffLocationType>("roundTrip"); |
|||
const [flightClassState, setFlightClassState] = useState("Economy"); |
|||
|
|||
const [guestAdultsInputValue, setGuestAdultsInputValue] = useState(2); |
|||
const [guestChildrenInputValue, setGuestChildrenInputValue] = useState(1); |
|||
const [guestInfantsInputValue, setGuestInfantsInputValue] = useState(1); |
|||
|
|||
const handleChangeData = (value: number, type: keyof GuestsObject) => { |
|||
let newValue = { |
|||
guestAdults: guestAdultsInputValue, |
|||
guestChildren: guestChildrenInputValue, |
|||
guestInfants: guestInfantsInputValue, |
|||
}; |
|||
if (type === "guestAdults") { |
|||
setGuestAdultsInputValue(value); |
|||
newValue.guestAdults = value; |
|||
} |
|||
if (type === "guestChildren") { |
|||
setGuestChildrenInputValue(value); |
|||
newValue.guestChildren = value; |
|||
} |
|||
if (type === "guestInfants") { |
|||
setGuestInfantsInputValue(value); |
|||
newValue.guestInfants = value; |
|||
} |
|||
}; |
|||
|
|||
const totalGuests = |
|||
guestChildrenInputValue + guestAdultsInputValue + guestInfantsInputValue; |
|||
|
|||
const renderGuest = () => { |
|||
return ( |
|||
<Popover className="relative"> |
|||
{({ open }) => ( |
|||
<> |
|||
<Popover.Button |
|||
as="button" |
|||
className={`
|
|||
${open ? "" : ""} |
|||
px-4 py-1.5 rounded-md inline-flex items-center font-medium hover:text-opacity-100 focus:outline-none focus-visible:ring-2 focus-visible:ring-white focus-visible:ring-opacity-75 text-xs`}
|
|||
> |
|||
<span>{`${totalGuests || ""} Guests`}</span> |
|||
<ChevronDownIcon |
|||
className={`${ |
|||
open ? "" : "text-opacity-70" |
|||
} ml-2 h-4 w-4 group-hover:text-opacity-80 transition ease-in-out duration-150`}
|
|||
aria-hidden="true" |
|||
/> |
|||
</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-20 w-full sm:min-w-[340px] max-w-sm bg-white dark:bg-neutral-800 top-full mt-3 left-1/2 -translate-x-1/2 py-5 sm:py-6 px-4 sm:px-8 rounded-3xl shadow-xl ring-1 ring-black/5 dark:ring-white/10"> |
|||
<NcInputNumber |
|||
className="w-full" |
|||
defaultValue={guestAdultsInputValue} |
|||
onChange={(value) => handleChangeData(value, "guestAdults")} |
|||
max={10} |
|||
min={1} |
|||
label="Adults" |
|||
desc="Ages 13 or above" |
|||
/> |
|||
<NcInputNumber |
|||
className="w-full mt-6" |
|||
defaultValue={guestChildrenInputValue} |
|||
onChange={(value) => handleChangeData(value, "guestChildren")} |
|||
max={4} |
|||
label="Children" |
|||
desc="Ages 2–12" |
|||
/> |
|||
|
|||
<NcInputNumber |
|||
className="w-full mt-6" |
|||
defaultValue={guestInfantsInputValue} |
|||
onChange={(value) => handleChangeData(value, "guestInfants")} |
|||
max={4} |
|||
label="Infants" |
|||
desc="Ages 0–2" |
|||
/> |
|||
</Popover.Panel> |
|||
</Transition> |
|||
</> |
|||
)} |
|||
</Popover> |
|||
); |
|||
}; |
|||
|
|||
const renderSelectClass = () => { |
|||
return ( |
|||
<Popover className="relative"> |
|||
{({ open, close }) => ( |
|||
<> |
|||
<Popover.Button |
|||
className={`
|
|||
${open ? "" : ""} |
|||
px-4 py-1.5 rounded-md inline-flex items-center font-medium hover:text-opacity-100 focus:outline-none focus-visible:ring-2 focus-visible:ring-white focus-visible:ring-opacity-75 text-xs`}
|
|||
> |
|||
<span>{`${flightClassState}`}</span> |
|||
<ChevronDownIcon |
|||
className={`${ |
|||
open ? "" : "text-opacity-70" |
|||
} ml-2 h-4 w-4 group-hover:text-opacity-80 transition ease-in-out duration-150`}
|
|||
aria-hidden="true" |
|||
/> |
|||
</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-20 w-screen max-w-[200px] sm:max-w-[220px] px-4 top-full mt-3 transform -translate-x-1/2 left-1/2 sm:px-0 "> |
|||
<div className="overflow-hidden rounded-2xl shadow-lg ring-1 ring-black/5 dark:ring-white/10 "> |
|||
<div className="relative grid gap-8 bg-white dark:bg-neutral-800 p-7 "> |
|||
{flightClass.map((item) => ( |
|||
<a |
|||
key={item.name} |
|||
href={item.href} |
|||
onClick={(e) => { |
|||
e.preventDefault(); |
|||
setFlightClassState(item.name); |
|||
close(); |
|||
}} |
|||
className="flex items-center p-2 -m-3 transition duration-150 ease-in-out rounded-lg hover:bg-gray-50 dark:hover:bg-gray-700 focus:outline-none focus-visible:ring focus-visible:ring-orange-500 focus-visible:ring-opacity-50" |
|||
> |
|||
<p className="text-sm font-medium ">{item.name}</p> |
|||
</a> |
|||
))} |
|||
</div> |
|||
</div> |
|||
</Popover.Panel> |
|||
</Transition> |
|||
</> |
|||
)} |
|||
</Popover> |
|||
); |
|||
}; |
|||
|
|||
const renderRadioBtn = () => { |
|||
return ( |
|||
<div className=" py-5 [ nc-hero-field-padding ] flex flex-row flex-wrap border-b border-neutral-100 dark:border-neutral-700"> |
|||
<div |
|||
className={`py-1.5 px-4 flex items-center rounded-full font-medium text-xs cursor-pointer mr-2 my-1 sm:mr-3 ${ |
|||
dropOffLocationType === "roundTrip" |
|||
? "bg-black shadow-black/10 shadow-lg text-white" |
|||
: "border border-neutral-300 dark:border-neutral-700" |
|||
}`}
|
|||
onClick={(e) => setDropOffLocationType("roundTrip")} |
|||
> |
|||
Round-trip |
|||
</div> |
|||
<div |
|||
className={`py-1.5 px-4 flex items-center rounded-full font-medium text-xs cursor-pointer mr-2 my-1 sm:mr-3 ${ |
|||
dropOffLocationType === "oneWay" |
|||
? "bg-black text-white shadow-black/10 shadow-lg" |
|||
: "border border-neutral-300 dark:border-neutral-700" |
|||
}`}
|
|||
onClick={(e) => setDropOffLocationType("oneWay")} |
|||
> |
|||
One-way |
|||
</div> |
|||
|
|||
<div className="self-center border-r border-slate-200 dark:border-slate-700 h-8 mr-2 my-1 sm:mr-3"></div> |
|||
|
|||
<div className="mr-2 my-1 sm:mr-3 border border-neutral-300 dark:border-neutral-700 rounded-full"> |
|||
{renderSelectClass()} |
|||
</div> |
|||
<div className="my-1 border border-neutral-300 dark:border-neutral-700 rounded-full"> |
|||
{renderGuest()} |
|||
</div> |
|||
</div> |
|||
); |
|||
}; |
|||
|
|||
const renderForm = () => { |
|||
return ( |
|||
<form className="w-full relative mt-8 rounded-[40px] xl:rounded-[49px] rounded-t-2xl xl:rounded-t-3xl shadow-xl dark:shadow-2xl bg-white dark:bg-neutral-800"> |
|||
{renderRadioBtn()} |
|||
<div className="flex flex-1 rounded-full"> |
|||
<LocationInput |
|||
placeHolder="Flying from" |
|||
desc="Where do you want to fly from?" |
|||
className="flex-1" |
|||
/> |
|||
<div className="self-center border-r border-slate-200 dark:border-slate-700 h-8"></div> |
|||
<LocationInput |
|||
placeHolder="Flying to" |
|||
desc="Where you want to fly to?" |
|||
className="flex-1" |
|||
divHideVerticalLineClass=" -inset-x-0.5" |
|||
/> |
|||
<div className="self-center border-r border-slate-200 dark:border-slate-700 h-8"></div> |
|||
<FlightDateRangeInput |
|||
selectsRange={dropOffLocationType !== "oneWay"} |
|||
className="flex-1" |
|||
/> |
|||
</div> |
|||
</form> |
|||
); |
|||
}; |
|||
|
|||
return renderForm(); |
|||
}; |
|||
|
|||
export default FlightSearchForm; |
@ -1,66 +0,0 @@ |
|||
"use client"; |
|||
|
|||
import React, { FC, useState } from "react"; |
|||
import RealEstateSearchForm from "./RealEstateSearchForm"; |
|||
|
|||
export type SearchRealEstateTab = "Buy" | "Rent" | "Sell"; |
|||
|
|||
export interface HeroRealEstateSearchFormProps { |
|||
className?: string; |
|||
currentTab?: SearchRealEstateTab; |
|||
} |
|||
|
|||
const HeroRealEstateSearchForm: FC<HeroRealEstateSearchFormProps> = ({ |
|||
className = "", |
|||
currentTab = "Buy", |
|||
}) => { |
|||
const tabs: SearchRealEstateTab[] = ["Buy", "Rent", "Sell"]; |
|||
const [tabActive, setTabActive] = useState<SearchRealEstateTab>(currentTab); |
|||
|
|||
const renderTab = () => { |
|||
return ( |
|||
<ul className="ml-6 md:ml-16 xl:ml-20 inline-flex space-x-4 sm:space-x-8 lg:space-x-10 bg-white dark:bg-neutral-900 pb-6 md:p-6 !pl-0 xl:p-0 rounded-t-3xl"> |
|||
{tabs.map((tab) => { |
|||
const active = tab === tabActive; |
|||
return ( |
|||
<li |
|||
onClick={() => setTabActive(tab)} |
|||
className={`flex items-center cursor-pointer text-sm lg:text-base font-medium ${ |
|||
active |
|||
? "" |
|||
: "text-neutral-500 hover:text-neutral-700 dark:hover:text-neutral-100" |
|||
} `}
|
|||
key={tab} |
|||
> |
|||
{active && ( |
|||
<span className="block w-2.5 h-2.5 rounded-full bg-neutral-800 dark:bg-neutral-100 mr-2" /> |
|||
)} |
|||
<span>{tab}</span> |
|||
</li> |
|||
); |
|||
})} |
|||
</ul> |
|||
); |
|||
}; |
|||
|
|||
const renderForm = () => { |
|||
switch (tabActive) { |
|||
case "Buy": |
|||
return <RealEstateSearchForm />; |
|||
|
|||
default: |
|||
return <RealEstateSearchForm />; |
|||
} |
|||
}; |
|||
|
|||
return ( |
|||
<div |
|||
className={`nc-HeroRealEstateSearchForm w-full max-w-6xl py-5 lg:py-0 ${className}`} |
|||
> |
|||
{renderTab()} |
|||
{renderForm()} |
|||
</div> |
|||
); |
|||
}; |
|||
|
|||
export default HeroRealEstateSearchForm; |
@ -1,137 +0,0 @@ |
|||
"use client"; |
|||
|
|||
import React, { Fragment, useState, FC } from "react"; |
|||
import { Popover, Transition } from "@headlessui/react"; |
|||
import Slider from "rc-slider"; |
|||
import convertNumbThousand from "@/utils/convertNumbThousand"; |
|||
import ButtonSubmit from "../ButtonSubmit"; |
|||
import { CurrencyDollarIcon } from "@heroicons/react/24/outline"; |
|||
|
|||
export interface PriceRangeInputProps { |
|||
onChange?: (data: any) => void; |
|||
fieldClassName?: string; |
|||
} |
|||
|
|||
const PriceRangeInput: FC<PriceRangeInputProps> = ({ |
|||
onChange, |
|||
fieldClassName = "[ nc-hero-field-padding ]", |
|||
}) => { |
|||
const [rangePrices, setRangePrices] = useState([100000, 4000000]); |
|||
|
|||
return ( |
|||
<Popover className="flex relative flex-[1.3]"> |
|||
{({ open, close }) => ( |
|||
<> |
|||
<div |
|||
className={`flex-1 z-10 flex items-center focus:outline-none cursor-pointer ${ |
|||
open ? "nc-hero-field-focused" : "" |
|||
}`}
|
|||
> |
|||
<Popover.Button |
|||
className={`flex-1 flex text-left items-center focus:outline-none ${fieldClassName} space-x-3 `} |
|||
onClickCapture={() => document.querySelector("html")?.click()} |
|||
> |
|||
<div className="text-neutral-300 dark:text-neutral-400"> |
|||
<CurrencyDollarIcon className="w-5 h-5 lg:w-7 lg:h-7" /> |
|||
</div> |
|||
<div className="flex-grow"> |
|||
<span className="block xl:text-lg font-semibold truncate"> |
|||
{`$${convertNumbThousand( |
|||
rangePrices[0] / 1000 |
|||
)}k ~ $${convertNumbThousand(rangePrices[1] / 1000)}k`}
|
|||
</span> |
|||
<span className="block mt-1 text-sm text-neutral-400 leading-none font-light "> |
|||
Choose price range |
|||
</span> |
|||
</div> |
|||
</Popover.Button> |
|||
|
|||
{/* BUTTON SUBMIT OF FORM */} |
|||
<div className="pr-2 xl:pr-4"> |
|||
<ButtonSubmit href="/listing-real-estate" /> |
|||
</div> |
|||
</div> |
|||
|
|||
{open && ( |
|||
<div className="h-8 absolute self-center top-1/2 -translate-y-1/2 z-0 -left-0.5 right-1 bg-white dark:bg-neutral-800"></div> |
|||
)} |
|||
|
|||
<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 left-0 lg:right-0 z-10 w-full sm:min-w-[340px] max-w-sm bg-white dark:bg-neutral-800 top-full mt-3 py-5 sm:py-6 px-4 sm:px-8 rounded-3xl shadow-xl"> |
|||
<div className="relative flex flex-col space-y-8"> |
|||
<div className="space-y-5"> |
|||
<span className="font-medium">Range Price </span> |
|||
<Slider |
|||
range |
|||
className="text-red-400" |
|||
min={10000} |
|||
max={10000000} |
|||
defaultValue={[rangePrices[0], rangePrices[1]]} |
|||
allowCross={false} |
|||
step={1000} |
|||
onChange={(e) => setRangePrices(e as number[])} |
|||
/> |
|||
</div> |
|||
|
|||
<div className="flex justify-between space-x-3"> |
|||
<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" |
|||
disabled |
|||
name="minPrice" |
|||
id="minPrice" |
|||
className="focus:ring-primary-500 focus:border-primary-500 block w-full pl-7 pr-3 sm:text-sm border-neutral-200 dark:border-neutral-500 rounded-full text-neutral-900 dark:text-neutral-200 bg-transparent" |
|||
value={convertNumbThousand(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 |
|||
disabled |
|||
type="text" |
|||
name="maxPrice" |
|||
id="maxPrice" |
|||
className="focus:ring-primary-500 focus:border-priring-primary-500 block w-full pl-7 pr-3 sm:text-sm border-neutral-200 dark:border-neutral-500 rounded-full text-neutral-900 dark:text-neutral-200 bg-transparent" |
|||
value={convertNumbThousand(rangePrices[1])} |
|||
/> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</Popover.Panel> |
|||
</Transition> |
|||
</> |
|||
)} |
|||
</Popover> |
|||
); |
|||
}; |
|||
|
|||
export default PriceRangeInput; |
@ -1,126 +0,0 @@ |
|||
"use client"; |
|||
import React, { Fragment, FC } from "react"; |
|||
import { Popover, Transition } from "@headlessui/react"; |
|||
import Checkbox from "@/shared/Checkbox"; |
|||
import { ClassOfProperties } from "../../type"; |
|||
import { HomeIcon } from "@heroicons/react/24/outline"; |
|||
|
|||
const defaultPropertyType: ClassOfProperties[] = [ |
|||
{ |
|||
name: "Duplex House", |
|||
description: "Have a place to yourself", |
|||
checked: true, |
|||
}, |
|||
{ |
|||
name: "Ferme House", |
|||
description: "Have your own room and share some common spaces", |
|||
checked: false, |
|||
}, |
|||
{ |
|||
name: "Chalet House", |
|||
description: |
|||
"Have a private or shared room in a boutique hotel, hostel, and more", |
|||
checked: false, |
|||
}, |
|||
{ |
|||
name: "Maison House", |
|||
description: "Stay in a shared space, like a common room", |
|||
checked: false, |
|||
}, |
|||
]; |
|||
|
|||
export interface PropertyTypeSelectProps { |
|||
onChange?: (data: any) => void; |
|||
fieldClassName?: string; |
|||
} |
|||
|
|||
const PropertyTypeSelect: FC<PropertyTypeSelectProps> = ({ |
|||
onChange, |
|||
fieldClassName = "[ nc-hero-field-padding ]", |
|||
}) => { |
|||
const [typeOfProperty, setTypeOfProperty] = |
|||
React.useState<ClassOfProperties[]>(defaultPropertyType); |
|||
|
|||
let typeOfPropertyText = ""; |
|||
if (typeOfProperty && typeOfProperty.length > 0) { |
|||
typeOfPropertyText = typeOfProperty |
|||
.filter((item) => item.checked) |
|||
.map((item) => { |
|||
return item.name; |
|||
}) |
|||
.join(", "); |
|||
} |
|||
return ( |
|||
<Popover className="flex relative flex-1"> |
|||
{({ open, close }) => ( |
|||
<> |
|||
<Popover.Button |
|||
className={`flex z-10 text-left w-full flex-shrink-0 items-center ${fieldClassName} space-x-3 focus:outline-none cursor-pointer ${ |
|||
open ? "nc-hero-field-focused" : "" |
|||
}`}
|
|||
onClickCapture={() => document.querySelector("html")?.click()} |
|||
> |
|||
<div className="text-neutral-300 dark:text-neutral-400"> |
|||
<HomeIcon className="w-5 h-5 lg:w-7 lg:h-7" /> |
|||
</div> |
|||
<div className="flex-1"> |
|||
<span className="block xl:text-lg font-semibold overflow-hidden"> |
|||
<span className="line-clamp-1"> |
|||
{typeOfPropertyText || `Type`} |
|||
</span> |
|||
</span> |
|||
<span className="block mt-1 text-sm text-neutral-400 leading-none font-light "> |
|||
Property type |
|||
</span> |
|||
</div> |
|||
</Popover.Button> |
|||
|
|||
{open && ( |
|||
<div className="h-8 absolute self-center top-1/2 -translate-y-1/2 z-0 -inset-x-0.5 bg-white dark:bg-neutral-800"></div> |
|||
)} |
|||
|
|||
<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 left-0 z-10 w-full sm:min-w-[340px] max-w-sm bg-white dark:bg-neutral-800 top-full mt-3 py-5 sm:py-6 px-4 sm:px-8 rounded-3xl shadow-xl"> |
|||
<div className=""> |
|||
<div className="relative flex flex-col space-y-5"> |
|||
{typeOfProperty.map((item, index) => ( |
|||
<div key={item.name} className=""> |
|||
<Checkbox |
|||
name={item.name} |
|||
label={item.name} |
|||
subLabel={item.description} |
|||
defaultChecked={typeOfProperty[index].checked} |
|||
onChange={(e) => { |
|||
const newState = typeOfProperty.map((item, i) => { |
|||
if (i === index) { |
|||
return { ...item, checked: e }; |
|||
} |
|||
return item; |
|||
}); |
|||
setTypeOfProperty(() => { |
|||
return newState; |
|||
}); |
|||
onChange && onChange(newState); |
|||
}} |
|||
/> |
|||
</div> |
|||
))} |
|||
</div> |
|||
</div> |
|||
</Popover.Panel> |
|||
</Transition> |
|||
</> |
|||
)} |
|||
</Popover> |
|||
); |
|||
}; |
|||
|
|||
export default PropertyTypeSelect; |
@ -1,25 +0,0 @@ |
|||
import React, { FC } from "react"; |
|||
import LocationInput from "../LocationInput"; |
|||
import PriceRangeInput from "./PriceRangeInput"; |
|||
import PropertyTypeSelect from "./PropertyTypeSelect"; |
|||
|
|||
export interface RealEstateSearchFormProps {} |
|||
|
|||
const RealEstateSearchForm: FC<RealEstateSearchFormProps> = ({}) => { |
|||
const renderForm = () => { |
|||
return ( |
|||
<form className="w-full relative xl:mt-8 flex flex-col lg:flex-row lg:items-center rounded-3xl lg:rounded-full shadow-xl dark:shadow-2xl bg-white dark:bg-neutral-800 divide-y divide-neutral-200 dark:divide-neutral-700 lg:divide-y-0"> |
|||
<LocationInput className="flex-[1.5]" /> |
|||
|
|||
<div className="self-center border-r border-slate-200 dark:border-slate-700 h-8"></div> |
|||
<PropertyTypeSelect /> |
|||
<div className="self-center border-r border-slate-200 dark:border-slate-700 h-8"></div> |
|||
<PriceRangeInput /> |
|||
</form> |
|||
); |
|||
}; |
|||
|
|||
return renderForm(); |
|||
}; |
|||
|
|||
export default RealEstateSearchForm; |
@ -1,129 +0,0 @@ |
|||
"use client"; |
|||
|
|||
import React, { Fragment, useState, FC } from "react"; |
|||
import DatePicker from "react-datepicker"; |
|||
import { Popover, Transition } from "@headlessui/react"; |
|||
import DatePickerCustomHeaderTwoMonth from "@/components/DatePickerCustomHeaderTwoMonth"; |
|||
import DatePickerCustomDay from "@/components/DatePickerCustomDay"; |
|||
import ClearDataButton from "../ClearDataButton"; |
|||
import ButtonSubmit from "../ButtonSubmit"; |
|||
|
|||
export interface RentalCarDatesRangeInputProps { |
|||
className?: string; |
|||
fieldClassName?: string; |
|||
hasButtonSubmit?: boolean; |
|||
} |
|||
|
|||
const RentalCarDatesRangeInput: FC<RentalCarDatesRangeInputProps> = ({ |
|||
className = "", |
|||
fieldClassName = "[ nc-hero-field-padding--small ]", |
|||
hasButtonSubmit = true, |
|||
}) => { |
|||
const [startDate, setStartDate] = useState<Date | null>( |
|||
new Date("2023/03/01") |
|||
); |
|||
const [endDate, setEndDate] = useState<Date | null>(new Date("2023/03/16")); |
|||
|
|||
const onChangeDate = (dates: [Date | null, Date | null]) => { |
|||
const [start, end] = dates; |
|||
setStartDate(start); |
|||
setEndDate(end); |
|||
}; |
|||
|
|||
const renderInput = () => { |
|||
return ( |
|||
<> |
|||
<div className="flex-grow text-left"> |
|||
<span className="block xl:text-base font-semibold"> |
|||
{startDate?.toLocaleDateString("en-US", { |
|||
month: "short", |
|||
day: "2-digit", |
|||
}) || "Add dates"} |
|||
{endDate |
|||
? " - " + |
|||
endDate?.toLocaleDateString("en-US", { |
|||
month: "short", |
|||
day: "2-digit", |
|||
}) |
|||
: ""} |
|||
</span> |
|||
<span className="block mt-1 text-sm text-neutral-400 leading-none font-light"> |
|||
{"Pick up - Drop off"} |
|||
</span> |
|||
</div> |
|||
</> |
|||
); |
|||
}; |
|||
|
|||
return ( |
|||
<> |
|||
<Popover |
|||
className={`RentalCarDatesRangeInput relative flex ${className}`} |
|||
> |
|||
{({ open }) => ( |
|||
<> |
|||
<div |
|||
className={`flex-1 z-10 flex items-center focus:outline-none ${ |
|||
open ? "nc-hero-field-focused--2" : "" |
|||
}`}
|
|||
> |
|||
<Popover.Button |
|||
className={`flex-1 z-10 flex relative ${fieldClassName} items-center space-x-3 focus:outline-none `} |
|||
> |
|||
{renderInput()} |
|||
|
|||
{startDate && open && ( |
|||
<ClearDataButton onClick={() => onChangeDate([null, null])} /> |
|||
)} |
|||
</Popover.Button> |
|||
|
|||
{/* BUTTON SUBMIT OF FORM */} |
|||
{hasButtonSubmit && ( |
|||
<div className="pr-2 xl:pr-4"> |
|||
<ButtonSubmit href="/listing-car-detail" /> |
|||
</div> |
|||
)} |
|||
</div> |
|||
|
|||
{open && ( |
|||
<div className="h-8 absolute self-center top-1/2 -translate-y-1/2 z-0 -inset-x-0.5 bg-white dark:bg-neutral-800"></div> |
|||
)} |
|||
|
|||
<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 left-1/2 z-10 mt-3 top-full w-screen max-w-sm -translate-x-1/2 transform px-4 sm:px-0 lg:max-w-3xl"> |
|||
<div className="overflow-hidden rounded-3xl shadow-lg ring-1 ring-black ring-opacity-5 bg-white dark:bg-neutral-800 p-8"> |
|||
<DatePicker |
|||
selected={startDate} |
|||
onChange={onChangeDate} |
|||
startDate={startDate} |
|||
endDate={endDate} |
|||
selectsRange |
|||
monthsShown={2} |
|||
showPopperArrow={false} |
|||
inline |
|||
renderCustomHeader={(p) => ( |
|||
<DatePickerCustomHeaderTwoMonth {...p} /> |
|||
)} |
|||
renderDayContents={(day, date) => ( |
|||
<DatePickerCustomDay dayOfMonth={day} date={date} /> |
|||
)} |
|||
/> |
|||
</div> |
|||
</Popover.Panel> |
|||
</Transition> |
|||
</> |
|||
)} |
|||
</Popover> |
|||
</> |
|||
); |
|||
}; |
|||
|
|||
export default RentalCarDatesRangeInput; |
@ -1,72 +0,0 @@ |
|||
"use client"; |
|||
|
|||
import React, { FC, useState } from "react"; |
|||
import LocationInput from "../LocationInput"; |
|||
import RentalCarDatesRangeInput from "./RentalCarDatesRangeInput"; |
|||
|
|||
export interface RentalCarSearchFormProps {} |
|||
|
|||
const RentalCarSearchForm: FC<RentalCarSearchFormProps> = ({}) => { |
|||
const [dropOffLocationType, setDropOffLocationType] = useState< |
|||
"same" | "different" |
|||
>("different"); |
|||
|
|||
const renderRadioBtn = () => { |
|||
return ( |
|||
<div className="pb-3 flex justify-center items-center space-x-3"> |
|||
<div |
|||
className={`py-1.5 px-4 flex items-center rounded-full font-medium text-xs cursor-pointer ${ |
|||
dropOffLocationType === "same" |
|||
? "bg-black text-white shadow-black/10 shadow-lg" |
|||
: "border border-neutral-300 dark:border-neutral-700" |
|||
}`}
|
|||
onClick={(e) => setDropOffLocationType("same")} |
|||
> |
|||
Same drop off |
|||
</div> |
|||
<div |
|||
className={`py-1.5 px-4 flex items-center rounded-full font-medium text-xs cursor-pointer ${ |
|||
dropOffLocationType === "different" |
|||
? "bg-black text-white shadow-black/10 shadow-lg" |
|||
: "border border-neutral-300 dark:border-neutral-700" |
|||
}`}
|
|||
onClick={(e) => setDropOffLocationType("different")} |
|||
> |
|||
Different drop off |
|||
</div> |
|||
</div> |
|||
); |
|||
}; |
|||
|
|||
const renderForm = () => { |
|||
return ( |
|||
<form className="w-full relative "> |
|||
{renderRadioBtn()} |
|||
<div className="flex flex-row w-full rounded-full border border-neutral-200 dark:border-neutral-700 bg-white dark:bg-neutral-800"> |
|||
<LocationInput |
|||
placeHolder="City or Airport" |
|||
desc="Pick up location" |
|||
className="flex-1" |
|||
/> |
|||
{dropOffLocationType === "different" && ( |
|||
<> |
|||
<div className="self-center border-r border-slate-200 dark:border-slate-700 h-8"></div> |
|||
<LocationInput |
|||
placeHolder="City or Airport" |
|||
desc="Drop off location" |
|||
className="flex-1" |
|||
divHideVerticalLineClass="-inset-x-0.5" |
|||
/> |
|||
</> |
|||
)} |
|||
<div className="self-center border-r border-slate-200 dark:border-slate-700 h-8"></div> |
|||
<RentalCarDatesRangeInput className="flex-1" /> |
|||
</div> |
|||
</form> |
|||
); |
|||
}; |
|||
|
|||
return renderForm(); |
|||
}; |
|||
|
|||
export default RentalCarSearchForm; |
@ -1,114 +0,0 @@ |
|||
"use client"; |
|||
|
|||
import React, { Fragment, useState, FC } from "react"; |
|||
import DatePicker from "react-datepicker"; |
|||
import { Popover, Transition } from "@headlessui/react"; |
|||
import DatePickerCustomHeaderTwoMonth from "@/components/DatePickerCustomHeaderTwoMonth"; |
|||
import DatePickerCustomDay from "@/components/DatePickerCustomDay"; |
|||
import ClearDataButton from "../ClearDataButton"; |
|||
|
|||
export interface ExperiencesDateSingleInputProps { |
|||
className?: string; |
|||
fieldClassName?: string; |
|||
} |
|||
|
|||
const ExperiencesDateSingleInput: FC<ExperiencesDateSingleInputProps> = ({ |
|||
className = "", |
|||
fieldClassName = "[ nc-hero-field-padding--small ]", |
|||
}) => { |
|||
const [startDate, setStartDate] = useState<Date | null>( |
|||
new Date("2023/03/01") |
|||
); |
|||
const [endDate, setEndDate] = useState<Date | null>(new Date("2023/03/16")); |
|||
|
|||
const onChangeDate = (dates: [Date | null, Date | null]) => { |
|||
const [start, end] = dates; |
|||
setStartDate(start); |
|||
setEndDate(end); |
|||
}; |
|||
|
|||
const renderInput = () => { |
|||
return ( |
|||
<> |
|||
<div className="flex-grow text-left"> |
|||
<span className="block xl:text-base font-semibold"> |
|||
{startDate?.toLocaleDateString("en-US", { |
|||
month: "short", |
|||
day: "2-digit", |
|||
}) || "Date"} |
|||
{endDate |
|||
? " - " + |
|||
endDate?.toLocaleDateString("en-US", { |
|||
month: "short", |
|||
day: "2-digit", |
|||
}) |
|||
: ""} |
|||
</span> |
|||
<span className="block mt-1 text-sm text-neutral-400 leading-none font-light"> |
|||
{startDate ? "Date" : `Add dates`} |
|||
</span> |
|||
</div> |
|||
</> |
|||
); |
|||
}; |
|||
|
|||
return ( |
|||
<> |
|||
<Popover |
|||
className={`ExperiencesDateSingleInput relative flex ${className}`} |
|||
> |
|||
{({ open }) => ( |
|||
<> |
|||
<Popover.Button |
|||
className={`flex-1 z-10 flex relative ${fieldClassName} items-center space-x-3 focus:outline-none ${ |
|||
open ? "nc-hero-field-focused--2" : "" |
|||
}`}
|
|||
> |
|||
{renderInput()} |
|||
{startDate && open && ( |
|||
<ClearDataButton onClick={() => onChangeDate([null, null])} /> |
|||
)} |
|||
</Popover.Button> |
|||
|
|||
{open && ( |
|||
<div className="h-8 absolute self-center top-1/2 -translate-y-1/2 z-0 -inset-x-0.5 bg-white dark:bg-neutral-800"></div> |
|||
)} |
|||
|
|||
<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 left-1/2 z-10 mt-3 top-full w-screen max-w-sm -translate-x-1/2 transform px-4 sm:px-0 lg:max-w-3xl"> |
|||
<div className="overflow-hidden rounded-3xl shadow-lg ring-1 ring-black ring-opacity-5 bg-white dark:bg-neutral-800 p-8"> |
|||
<DatePicker |
|||
selected={startDate} |
|||
onChange={onChangeDate} |
|||
startDate={startDate} |
|||
endDate={endDate} |
|||
selectsRange |
|||
monthsShown={2} |
|||
showPopperArrow={false} |
|||
inline |
|||
renderCustomHeader={(p) => ( |
|||
<DatePickerCustomHeaderTwoMonth {...p} /> |
|||
)} |
|||
renderDayContents={(day, date) => ( |
|||
<DatePickerCustomDay dayOfMonth={day} date={date} /> |
|||
)} |
|||
/> |
|||
</div> |
|||
</Popover.Panel> |
|||
</Transition> |
|||
</> |
|||
)} |
|||
</Popover> |
|||
</> |
|||
); |
|||
}; |
|||
|
|||
export default ExperiencesDateSingleInput; |
@ -1,29 +0,0 @@ |
|||
"use client"; |
|||
|
|||
import React, { FC } from "react"; |
|||
import LocationInput from "../LocationInput"; |
|||
import GuestsInput from "../GuestsInput"; |
|||
import ExperiencesDateSingleInput from "./ExperiencesDateSingleInput"; |
|||
|
|||
export interface ExperiencesSearchFormProps {} |
|||
|
|||
const ExperiencesSearchForm: FC<ExperiencesSearchFormProps> = ({}) => { |
|||
const renderForm = () => { |
|||
return ( |
|||
<form className="w-full relative flex flex-row rounded-full border border-neutral-200 dark:border-neutral-700 bg-white dark:bg-neutral-800"> |
|||
<LocationInput |
|||
// onInputDone={() => setDateFocused(true)}
|
|||
className="flex-[1.5]" |
|||
/> |
|||
<div className="self-center border-r border-slate-200 dark:border-slate-700 h-8"></div> |
|||
<ExperiencesDateSingleInput className="flex-[1.2]" /> |
|||
<div className="self-center border-r border-slate-200 dark:border-slate-700 h-8"></div> |
|||
<GuestsInput className="flex-1" submitLink="/listing-experiences" /> |
|||
</form> |
|||
); |
|||
}; |
|||
|
|||
return renderForm(); |
|||
}; |
|||
|
|||
export default ExperiencesSearchForm; |
@ -1,148 +0,0 @@ |
|||
"use client"; |
|||
|
|||
import React, { Fragment, useState } from "react"; |
|||
import { FC } from "react"; |
|||
import DatePicker from "react-datepicker"; |
|||
import { Popover, Transition } from "@headlessui/react"; |
|||
import DatePickerCustomHeaderTwoMonth from "@/components/DatePickerCustomHeaderTwoMonth"; |
|||
import DatePickerCustomDay from "@/components/DatePickerCustomDay"; |
|||
import ClearDataButton from "../ClearDataButton"; |
|||
import ButtonSubmit from "../ButtonSubmit"; |
|||
|
|||
export interface FlightDateRangeInputProps { |
|||
className?: string; |
|||
fieldClassName?: string; |
|||
hasButtonSubmit?: boolean; |
|||
selectsRange?: boolean; |
|||
} |
|||
|
|||
const FlightDateRangeInput: FC<FlightDateRangeInputProps> = ({ |
|||
className = "", |
|||
fieldClassName = "[ nc-hero-field-padding--small ]", |
|||
hasButtonSubmit = true, |
|||
selectsRange = true, |
|||
}) => { |
|||
const [startDate, setStartDate] = useState<Date | null>( |
|||
new Date("2023/05/01") |
|||
); |
|||
const [endDate, setEndDate] = useState<Date | null>(new Date("2023/05/16")); |
|||
|
|||
const onChangeRangeDate = (dates: [Date | null, Date | null]) => { |
|||
const [start, end] = dates; |
|||
setStartDate(start); |
|||
setEndDate(end); |
|||
}; |
|||
|
|||
const renderInput = () => { |
|||
return ( |
|||
<> |
|||
<div className="flex-grow text-left"> |
|||
<span className="block xl:text-base font-semibold"> |
|||
{startDate?.toLocaleDateString("en-US", { |
|||
month: "short", |
|||
day: "2-digit", |
|||
}) || "Add dates"} |
|||
{selectsRange && endDate |
|||
? " - " + |
|||
endDate?.toLocaleDateString("en-US", { |
|||
month: "short", |
|||
day: "2-digit", |
|||
}) |
|||
: ""} |
|||
</span> |
|||
<span className="block mt-1 text-sm text-neutral-400 leading-none font-light"> |
|||
{selectsRange ? "Pick up - Drop off" : "Pick up date"} |
|||
</span> |
|||
</div> |
|||
</> |
|||
); |
|||
}; |
|||
|
|||
return ( |
|||
<> |
|||
<Popover className={`FlightDateRangeInput relative flex ${className}`}> |
|||
{({ open }) => ( |
|||
<> |
|||
<div |
|||
className={`flex-1 z-10 flex items-center focus:outline-none ${ |
|||
open ? "nc-hero-field-focused--2" : "" |
|||
}`}
|
|||
> |
|||
<Popover.Button |
|||
className={`flex-1 z-10 flex relative ${fieldClassName} items-center space-x-3 focus:outline-none `} |
|||
> |
|||
{renderInput()} |
|||
|
|||
{startDate && open && ( |
|||
<ClearDataButton |
|||
onClick={() => onChangeRangeDate([null, null])} |
|||
/> |
|||
)} |
|||
</Popover.Button> |
|||
|
|||
{/* BUTTON SUBMIT OF FORM */} |
|||
{hasButtonSubmit && ( |
|||
<div className="pr-2 xl:pr-4"> |
|||
<ButtonSubmit href="/listing-car-detail" /> |
|||
</div> |
|||
)} |
|||
</div> |
|||
|
|||
{open && ( |
|||
<div className="h-8 absolute self-center top-1/2 -translate-y-1/2 z-0 -left-0.5 right-10 bg-white dark:bg-neutral-800"></div> |
|||
)} |
|||
|
|||
<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 left-1/2 z-20 mt-3 top-full w-screen max-w-sm -translate-x-1/2 transform px-4 sm:px-0 lg:max-w-3xl"> |
|||
<div className="overflow-hidden rounded-3xl shadow-lg ring-1 ring-black ring-opacity-5 bg-white dark:bg-neutral-800 p-8"> |
|||
{selectsRange ? ( |
|||
<DatePicker |
|||
selected={startDate} |
|||
onChange={onChangeRangeDate} |
|||
startDate={startDate} |
|||
endDate={endDate} |
|||
selectsRange |
|||
monthsShown={2} |
|||
showPopperArrow={false} |
|||
inline |
|||
renderCustomHeader={(p) => ( |
|||
<DatePickerCustomHeaderTwoMonth {...p} /> |
|||
)} |
|||
renderDayContents={(day, date) => ( |
|||
<DatePickerCustomDay dayOfMonth={day} date={date} /> |
|||
)} |
|||
/> |
|||
) : ( |
|||
<DatePicker |
|||
selected={startDate} |
|||
onChange={(date) => setStartDate(date)} |
|||
monthsShown={2} |
|||
showPopperArrow={false} |
|||
inline |
|||
renderCustomHeader={(p) => ( |
|||
<DatePickerCustomHeaderTwoMonth {...p} /> |
|||
)} |
|||
renderDayContents={(day, date) => ( |
|||
<DatePickerCustomDay dayOfMonth={day} date={date} /> |
|||
)} |
|||
/> |
|||
)} |
|||
</div> |
|||
</Popover.Panel> |
|||
</Transition> |
|||
</> |
|||
)} |
|||
</Popover> |
|||
</> |
|||
); |
|||
}; |
|||
|
|||
export default FlightDateRangeInput; |
@ -1,247 +0,0 @@ |
|||
"use client"; |
|||
|
|||
import React, { FC, useState } from "react"; |
|||
import LocationInput from "../LocationInput"; |
|||
import { Popover, Transition } from "@headlessui/react"; |
|||
import { ChevronDownIcon } from "@heroicons/react/24/solid"; |
|||
import { Fragment } from "react"; |
|||
import NcInputNumber from "@/components/NcInputNumber"; |
|||
import FlightDateRangeInput from "./FlightDateRangeInput"; |
|||
import { GuestsObject } from "../../type"; |
|||
|
|||
export interface FlightSearchFormProps {} |
|||
|
|||
const flightClass = [ |
|||
{ |
|||
name: "Economy", |
|||
href: "##", |
|||
}, |
|||
{ |
|||
name: "Business", |
|||
href: "##", |
|||
}, |
|||
{ |
|||
name: "Multiple", |
|||
href: "##", |
|||
}, |
|||
]; |
|||
|
|||
const FlightSearchForm: FC<FlightSearchFormProps> = ({}) => { |
|||
const [dropOffLocationType, setDropOffLocationType] = useState< |
|||
"roundTrip" | "oneWay" | "" |
|||
>("roundTrip"); |
|||
const [flightClassState, setFlightClassState] = useState("Economy"); |
|||
const [guestAdultsInputValue, setGuestAdultsInputValue] = useState(2); |
|||
const [guestChildrenInputValue, setGuestChildrenInputValue] = useState(1); |
|||
const [guestInfantsInputValue, setGuestInfantsInputValue] = useState(1); |
|||
|
|||
const handleChangeData = (value: number, type: keyof GuestsObject) => { |
|||
let newValue = { |
|||
guestAdults: guestAdultsInputValue, |
|||
guestChildren: guestChildrenInputValue, |
|||
guestInfants: guestInfantsInputValue, |
|||
}; |
|||
if (type === "guestAdults") { |
|||
setGuestAdultsInputValue(value); |
|||
newValue.guestAdults = value; |
|||
} |
|||
if (type === "guestChildren") { |
|||
setGuestChildrenInputValue(value); |
|||
newValue.guestChildren = value; |
|||
} |
|||
if (type === "guestInfants") { |
|||
setGuestInfantsInputValue(value); |
|||
newValue.guestInfants = value; |
|||
} |
|||
}; |
|||
|
|||
const totalGuests = |
|||
guestChildrenInputValue + guestAdultsInputValue + guestInfantsInputValue; |
|||
|
|||
const renderGuest = () => { |
|||
return ( |
|||
<div className=""> |
|||
<Popover className="relative"> |
|||
{({ open }) => ( |
|||
<> |
|||
<Popover.Button |
|||
className={`px-4 py-1.5 rounded-md inline-flex items-center font-medium hover:text-opacity-100 focus:outline-none focus-visible:ring-0 text-xs`} |
|||
> |
|||
<span>{`${totalGuests || ""} Guests`}</span> |
|||
<ChevronDownIcon |
|||
className={`${ |
|||
open ? "" : "text-opacity-70" |
|||
} ml-2 h-4 w-4 group-hover:text-opacity-80 transition ease-in-out duration-150`}
|
|||
aria-hidden="true" |
|||
/> |
|||
</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-20 w-full sm:min-w-[340px] max-w-sm bg-white dark:bg-neutral-800 top-full mt-3 left-1/2 -translate-x-1/2 py-5 sm:py-6 px-4 sm:px-8 rounded-3xl shadow-xl ring-1 ring-black/5 dark:ring-white/10"> |
|||
<NcInputNumber |
|||
className="w-full" |
|||
defaultValue={guestAdultsInputValue} |
|||
onChange={(value) => handleChangeData(value, "guestAdults")} |
|||
max={10} |
|||
min={1} |
|||
label="Adults" |
|||
desc="Ages 13 or above" |
|||
/> |
|||
<NcInputNumber |
|||
className="w-full mt-6" |
|||
defaultValue={guestChildrenInputValue} |
|||
onChange={(value) => |
|||
handleChangeData(value, "guestChildren") |
|||
} |
|||
max={4} |
|||
label="Children" |
|||
desc="Ages 2–12" |
|||
/> |
|||
|
|||
<NcInputNumber |
|||
className="w-full mt-6" |
|||
defaultValue={guestInfantsInputValue} |
|||
onChange={(value) => |
|||
handleChangeData(value, "guestInfants") |
|||
} |
|||
max={4} |
|||
label="Infants" |
|||
desc="Ages 0–2" |
|||
/> |
|||
</Popover.Panel> |
|||
</Transition> |
|||
</> |
|||
)} |
|||
</Popover> |
|||
</div> |
|||
); |
|||
}; |
|||
|
|||
const renderSelectClass = () => { |
|||
return ( |
|||
<div className=""> |
|||
<Popover className="relative"> |
|||
{({ open, close }) => ( |
|||
<> |
|||
<Popover.Button |
|||
className={`px-4 py-1.5 rounded-md inline-flex items-center font-medium hover:text-opacity-100 focus:outline-none focus-visible:ring-0 text-xs`} |
|||
> |
|||
<span>{`${flightClassState}`}</span> |
|||
<ChevronDownIcon |
|||
className={`${ |
|||
open ? "" : "text-opacity-70" |
|||
} ml-2 h-4 w-4 group-hover:text-opacity-80 transition ease-in-out duration-150`}
|
|||
aria-hidden="true" |
|||
/> |
|||
</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-30 w-screen max-w-[200px] sm:max-w-[220px] px-4 mt-3 transform -translate-x-1/2 left-1/2 sm:px-0 "> |
|||
<div className="overflow-hidden rounded-2xl shadow-lg ring-1 ring-black/5 dark:ring-white/10 "> |
|||
<div className="relative grid bg-white dark:bg-neutral-800 p-3"> |
|||
{flightClass.map((item) => ( |
|||
<a |
|||
key={item.name} |
|||
href={item.href} |
|||
onClick={(e) => { |
|||
e.preventDefault(); |
|||
setFlightClassState(item.name); |
|||
close(); |
|||
}} |
|||
className="flex items-center p-2 transition duration-150 ease-in-out rounded-lg hover:bg-neutral-100 dark:hover:bg-gray-700 focus:outline-none focus-visible:ring focus-visible:ring-orange-500 focus-visible:ring-opacity-50" |
|||
> |
|||
<p className="text-sm font-medium ">{item.name}</p> |
|||
</a> |
|||
))} |
|||
</div> |
|||
</div> |
|||
</Popover.Panel> |
|||
</Transition> |
|||
</> |
|||
)} |
|||
</Popover> |
|||
</div> |
|||
); |
|||
}; |
|||
|
|||
const renderRadioBtn = () => { |
|||
return ( |
|||
<div className="pb-3 flex justify-center space-x-3"> |
|||
<div |
|||
className={`py-1.5 px-4 flex items-center rounded-full font-medium text-xs cursor-pointer select-none ${ |
|||
dropOffLocationType === "roundTrip" |
|||
? "bg-black shadow-black/10 shadow-lg text-white" |
|||
: "border border-neutral-300 dark:border-neutral-700" |
|||
}`}
|
|||
onClick={(e) => setDropOffLocationType("roundTrip")} |
|||
> |
|||
Round-trip |
|||
</div> |
|||
<div |
|||
className={`py-1.5 px-4 flex items-center rounded-full font-medium text-xs cursor-pointer select-none ${ |
|||
dropOffLocationType === "oneWay" |
|||
? "bg-black text-white shadow-black/10 shadow-lg" |
|||
: "border border-neutral-300 dark:border-neutral-700" |
|||
}`}
|
|||
onClick={(e) => setDropOffLocationType("oneWay")} |
|||
> |
|||
One-way |
|||
</div> |
|||
|
|||
<div className=" border-r border-slate-200 dark:border-slate-700 "></div> |
|||
|
|||
<div className="border border-neutral-300 dark:border-neutral-700 rounded-full"> |
|||
{renderSelectClass()} |
|||
</div> |
|||
<div className="border border-neutral-300 dark:border-neutral-700 rounded-full"> |
|||
{renderGuest()} |
|||
</div> |
|||
</div> |
|||
); |
|||
}; |
|||
|
|||
const renderForm = () => { |
|||
return ( |
|||
<form className="w-full relative "> |
|||
{renderRadioBtn()} |
|||
<div className="flex w-full rounded-full border border-neutral-200 dark:border-neutral-700 bg-white dark:bg-neutral-800"> |
|||
<LocationInput |
|||
placeHolder="Flying from" |
|||
desc="Where do you want to fly from?" |
|||
className="flex-1" |
|||
/> |
|||
<div className="self-center border-r border-slate-200 dark:border-slate-700 h-8"></div> |
|||
<LocationInput |
|||
placeHolder="Flying to" |
|||
desc="Where you want to fly to?" |
|||
className="flex-1" |
|||
divHideVerticalLineClass=" -inset-x-0.5" |
|||
/> |
|||
<div className="self-center border-r border-slate-200 dark:border-slate-700 h-8"></div> |
|||
<FlightDateRangeInput |
|||
selectsRange={dropOffLocationType !== "oneWay"} |
|||
className="flex-1" |
|||
/> |
|||
</div> |
|||
</form> |
|||
); |
|||
}; |
|||
|
|||
return renderForm(); |
|||
}; |
|||
|
|||
export default FlightSearchForm; |
@ -1,51 +0,0 @@ |
|||
import React, { FC } from "react"; |
|||
import { DEMO_EXPERIENCES_LISTINGS } from "@/data/listings"; |
|||
import { ExperiencesDataType, StayDataType } from "@/data/types"; |
|||
import Pagination from "@/shared/Pagination"; |
|||
import TabFilters from "./TabFilters"; |
|||
import Heading2 from "@/shared/Heading2"; |
|||
import ExperiencesCard from "@/components/ExperiencesCard"; |
|||
|
|||
export interface SectionGridFilterCardProps { |
|||
className?: string; |
|||
data?: StayDataType[]; |
|||
} |
|||
|
|||
const DEMO_DATA: ExperiencesDataType[] = DEMO_EXPERIENCES_LISTINGS.filter( |
|||
(_, i) => i < 8 |
|||
); |
|||
|
|||
const SectionGridFilterCard: FC<SectionGridFilterCardProps> = ({ |
|||
className = "", |
|||
data = DEMO_DATA, |
|||
}) => { |
|||
return ( |
|||
<div className={`nc-SectionGridFilterCard ${className}`}> |
|||
<Heading2 |
|||
heading="Experiences in Tokyo" |
|||
subHeading={ |
|||
<span className="block text-neutral-500 dark:text-neutral-400 mt-3"> |
|||
233 experiences |
|||
<span className="mx-2">·</span> |
|||
Aug 12 - 18 |
|||
<span className="mx-2">·</span>2 Guests |
|||
</span> |
|||
} |
|||
/> |
|||
|
|||
<div className="mb-8 lg:mb-11"> |
|||
<TabFilters /> |
|||
</div> |
|||
<div className="grid grid-cols-1 gap-6 md:gap-8 sm:grid-cols-2 lg:grid-cols-3 2xl:grid-cols-4"> |
|||
{data.map((stay) => ( |
|||
<ExperiencesCard key={stay.id} data={stay} /> |
|||
))} |
|||
</div> |
|||
<div className="flex mt-16 justify-center items-center"> |
|||
<Pagination /> |
|||
</div> |
|||
</div> |
|||
); |
|||
}; |
|||
|
|||
export default SectionGridFilterCard; |
@ -1,113 +0,0 @@ |
|||
"use client"; |
|||
|
|||
import React, { FC, useState } from "react"; |
|||
import AnyReactComponent from "@/components/AnyReactComponent/AnyReactComponent"; |
|||
import GoogleMapReact from "google-map-react"; |
|||
import { DEMO_EXPERIENCES_LISTINGS } from "@/data/listings"; |
|||
import ButtonClose from "@/shared/ButtonClose"; |
|||
import Checkbox from "@/shared/Checkbox"; |
|||
import Pagination from "@/shared/Pagination"; |
|||
import TabFilters from "./TabFilters"; |
|||
import Heading2 from "@/shared/Heading2"; |
|||
import ExperiencesCardH from "@/components/ExperiencesCardH"; |
|||
|
|||
const DEMO_EXPERIENCES = DEMO_EXPERIENCES_LISTINGS.filter((_, i) => i < 12); |
|||
|
|||
export interface SectionGridHasMapProps {} |
|||
|
|||
const SectionGridHasMap: FC<SectionGridHasMapProps> = () => { |
|||
const [currentHoverID, setCurrentHoverID] = useState<string | number>(-1); |
|||
const [showFullMapFixed, setShowFullMapFixed] = useState(false); |
|||
|
|||
return ( |
|||
<div> |
|||
<div className="relative flex min-h-screen"> |
|||
{/* CARDSSSS */} |
|||
<div className="min-h-screen w-full xl:w-[780px] 2xl:w-[880px] flex-shrink-0 xl:px-8 "> |
|||
<Heading2 |
|||
heading="Experiences in Tokyo" |
|||
subHeading={ |
|||
<span className="block text-neutral-500 dark:text-neutral-400 mt-3"> |
|||
233 experiences |
|||
<span className="mx-2">·</span> |
|||
Aug 12 - 18 |
|||
<span className="mx-2">·</span>2 Guests |
|||
</span> |
|||
} |
|||
/> |
|||
<div className="mb-8 lg:mb-11"> |
|||
<TabFilters /> |
|||
</div> |
|||
<div className="grid grid-cols-1 gap-8"> |
|||
{DEMO_EXPERIENCES.map((item) => ( |
|||
<div |
|||
key={item.id} |
|||
onMouseEnter={() => setCurrentHoverID((_) => item.id)} |
|||
onMouseLeave={() => setCurrentHoverID((_) => -1)} |
|||
> |
|||
<ExperiencesCardH data={item} /> |
|||
</div> |
|||
))} |
|||
</div> |
|||
<div className="flex mt-16 justify-center items-center"> |
|||
<Pagination /> |
|||
</div> |
|||
</div> |
|||
|
|||
<div |
|||
className="flex xl:hidden items-center justify-center fixed bottom-8 left-1/2 transform -translate-x-1/2 px-6 py-2 bg-neutral-900 text-white shadow-2xl rounded-full z-30 space-x-3 text-sm cursor-pointer" |
|||
onClick={() => setShowFullMapFixed(true)} |
|||
> |
|||
<i className="text-lg las la-map"></i> |
|||
<span>Show map</span> |
|||
</div> |
|||
|
|||
{/* MAPPPPP */} |
|||
<div |
|||
className={`xl:flex-grow xl:static xl:block ${ |
|||
showFullMapFixed ? "fixed inset-0 z-50" : "hidden" |
|||
}`}
|
|||
> |
|||
{showFullMapFixed && ( |
|||
<ButtonClose |
|||
onClick={() => setShowFullMapFixed(false)} |
|||
className="bg-white absolute z-50 left-3 top-3 shadow-lg rounded-xl w-10 h-10" |
|||
/> |
|||
)} |
|||
|
|||
<div className="fixed xl:sticky top-0 xl:top-[88px] left-0 w-full h-full xl:h-[calc(100vh-88px)] rounded-md overflow-hidden"> |
|||
<div className="absolute bottom-5 left-3 lg:bottom-auto lg:top-2.5 lg:left-1/2 transform lg:-translate-x-1/2 py-2 px-4 bg-white shadow-xl z-10 rounded-2xl min-w-max"> |
|||
<Checkbox |
|||
className="text-xs xl:text-sm text-neutral-800" |
|||
name="xx" |
|||
label="Search as I move the map" |
|||
/> |
|||
</div> |
|||
{/* BELLOW IS MY GOOGLE API KEY -- PLEASE DELETE AND TYPE YOUR API KEY */} |
|||
|
|||
<GoogleMapReact |
|||
bootstrapURLKeys={{ |
|||
key: "AIzaSyAGVJfZMAKYfZ71nzL_v5i3LjTTWnCYwTY", |
|||
}} |
|||
yesIWantToUseGoogleMapApiInternals |
|||
defaultZoom={12} |
|||
defaultCenter={DEMO_EXPERIENCES[0].map} |
|||
> |
|||
{DEMO_EXPERIENCES.map((item) => ( |
|||
<AnyReactComponent |
|||
isSelected={currentHoverID === item.id} |
|||
key={item.id} |
|||
lat={item.map.lat} |
|||
lng={item.map.lng} |
|||
experiences={item} |
|||
/> |
|||
))} |
|||
</GoogleMapReact> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
); |
|||
}; |
|||
|
|||
export default SectionGridHasMap; |
@ -1,538 +0,0 @@ |
|||
"use client"; |
|||
|
|||
import React, { Fragment, useState } from "react"; |
|||
import { Dialog, Popover, Transition } from "@headlessui/react"; |
|||
import ButtonPrimary from "@/shared/ButtonPrimary"; |
|||
import ButtonThird from "@/shared/ButtonThird"; |
|||
import ButtonClose from "@/shared/ButtonClose"; |
|||
import Checkbox from "@/shared/Checkbox"; |
|||
import convertNumbThousand from "@/utils/convertNumbThousand"; |
|||
import Slider from "rc-slider"; |
|||
|
|||
// DEMO DATA
|
|||
const typeOfExpriences = [ |
|||
{ |
|||
name: "Food & drink", |
|||
description: "Short description for the experience", |
|||
}, |
|||
{ |
|||
name: "Art and culture", |
|||
description: "Short description for the experience", |
|||
}, |
|||
{ |
|||
name: "Nature and outdoors", |
|||
description: "Short description for the experience", |
|||
}, |
|||
{ |
|||
name: "Sports", |
|||
description: "Short description for the experience", |
|||
}, |
|||
]; |
|||
|
|||
const timeOfdays = [ |
|||
{ |
|||
name: "Morning", |
|||
description: "Start before 12pm", |
|||
}, |
|||
{ |
|||
name: "Afternoon", |
|||
description: "Start after 12pm", |
|||
}, |
|||
{ |
|||
name: "Evening", |
|||
description: "Start after 5pm", |
|||
}, |
|||
]; |
|||
|
|||
//
|
|||
const moreFilter1 = typeOfExpriences; |
|||
const moreFilter2 = timeOfdays; |
|||
|
|||
const TabFilters = () => { |
|||
const [isOpenMoreFilter, setisOpenMoreFilter] = useState(false); |
|||
//
|
|||
const [isOnSale, setIsOnSale] = useState(true); |
|||
const [rangePrices, setRangePrices] = useState([0, 1000]); |
|||
//
|
|||
const closeModalMoreFilter = () => setisOpenMoreFilter(false); |
|||
const openModalMoreFilter = () => setisOpenMoreFilter(true); |
|||
|
|||
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 experiences</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"> |
|||
{typeOfExpriences.map((item) => ( |
|||
<div key={item.name} className=""> |
|||
<Checkbox |
|||
name={item.name} |
|||
label={item.name} |
|||
subLabel={item.description} |
|||
/> |
|||
</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> |
|||
); |
|||
}; |
|||
|
|||
const renderTabsTimeOfDay = () => { |
|||
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>Time of day</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"> |
|||
{timeOfdays.map((item) => ( |
|||
<div key={item.name} className=""> |
|||
<Checkbox |
|||
name={item.name} |
|||
label={item.name} |
|||
subLabel={item.description} |
|||
/> |
|||
</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> |
|||
); |
|||
}; |
|||
|
|||
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 |
|||
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> |
|||
); |
|||
}; |
|||
|
|||
const renderTabOnSale = () => { |
|||
return ( |
|||
<div |
|||
className={`flex items-center justify-center px-4 py-2 text-sm rounded-full border focus:outline-none cursor-pointer transition-all ${ |
|||
isOnSale |
|||
? "border-primary-500 bg-primary-50 text-primary-700" |
|||
: "border-neutral-300 dark:border-neutral-700" |
|||
}`}
|
|||
onClick={() => setIsOnSale(!isOnSale)} |
|||
> |
|||
<span>On sale</span> |
|||
{isOnSale && renderXClear()} |
|||
</div> |
|||
); |
|||
}; |
|||
|
|||
const renderMoreFilterItem = ( |
|||
data: { |
|||
name: string; |
|||
description?: 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 sm:grid-cols-2 gap-8"> |
|||
<div className="flex flex-col space-y-5"> |
|||
{list1.map((item) => ( |
|||
<Checkbox |
|||
key={item.name} |
|||
name={item.name} |
|||
subLabel={item.description} |
|||
label={item.name} |
|||
defaultChecked={!!item.defaultChecked} |
|||
/> |
|||
))} |
|||
</div> |
|||
<div className="flex flex-col space-y-5"> |
|||
{list2.map((item) => ( |
|||
<Checkbox |
|||
key={item.name} |
|||
name={item.name} |
|||
subLabel={item.description} |
|||
label={item.name} |
|||
defaultChecked={!!item.defaultChecked} |
|||
/> |
|||
))} |
|||
</div> |
|||
</div> |
|||
); |
|||
}; |
|||
|
|||
const renderTabMobileFilter = () => { |
|||
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> |
|||
<span className="hidden sm:inline">Experiences</span> 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" |
|||
> |
|||
Experiences 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-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 experiences |
|||
</h3> |
|||
<div className="mt-6 relative "> |
|||
{renderMoreFilterItem(moreFilter1)} |
|||
</div> |
|||
</div> |
|||
<div className="py-7"> |
|||
<h3 className="text-xl font-medium">Time of day</h3> |
|||
<div className="mt-6 relative "> |
|||
{renderMoreFilterItem(moreFilter2)} |
|||
</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 |
|||
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> |
|||
</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={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> |
|||
); |
|||
}; |
|||
|
|||
return ( |
|||
<div className="flex lg:space-x-4"> |
|||
<div className="hidden lg:flex space-x-4"> |
|||
{renderTabsTypeOfPlace()} |
|||
{renderTabsPriceRage()} |
|||
{renderTabsTimeOfDay()} |
|||
{renderTabOnSale()} |
|||
</div> |
|||
<div className="flex lg:hidden space-x-4"> |
|||
{renderTabMobileFilter()} |
|||
{renderTabOnSale()} |
|||
</div> |
|||
</div> |
|||
); |
|||
}; |
|||
|
|||
export default TabFilters; |
@ -1,56 +0,0 @@ |
|||
import BackgroundSection from "@/components/BackgroundSection"; |
|||
import BgGlassmorphism from "@/components/BgGlassmorphism"; |
|||
import SectionGridAuthorBox from "@/components/SectionGridAuthorBox"; |
|||
import SectionSliderNewCategories from "@/components/SectionSliderNewCategories"; |
|||
import SectionSubscribe2 from "@/components/SectionSubscribe2"; |
|||
import React, { ReactNode } from "react"; |
|||
import SectionHeroArchivePage from "../(server-components)/SectionHeroArchivePage"; |
|||
|
|||
const Layout = ({ children }: { children: ReactNode }) => { |
|||
return ( |
|||
<div className={`nc-ListingStayPage relative `}> |
|||
<BgGlassmorphism /> |
|||
|
|||
{/* SECTION HERO */} |
|||
<div className="container pt-10 pb-24 lg:pt-16 lg:pb-28"> |
|||
<SectionHeroArchivePage |
|||
currentPage="Experiences" |
|||
currentTab="Experiences" |
|||
listingType={ |
|||
<> |
|||
<i className="text-2xl las la-umbrella-beach"></i> |
|||
<span className="ml-2.5">1599 experiences</span> |
|||
</> |
|||
} |
|||
/> |
|||
</div> |
|||
|
|||
{children} |
|||
|
|||
<div className="container overflow-hidden"> |
|||
{/* SECTION 1 */} |
|||
<div className="relative py-16"> |
|||
<BackgroundSection /> |
|||
<SectionSliderNewCategories |
|||
heading="Explore by types of stays" |
|||
subHeading="Explore houses based on 10 types of stays" |
|||
categoryCardType="card5" |
|||
itemPerRow={5} |
|||
sliderStyle="style2" |
|||
/> |
|||
</div> |
|||
|
|||
{/* SECTION */} |
|||
<SectionSubscribe2 className="py-24 lg:py-28" /> |
|||
|
|||
{/* SECTION */} |
|||
<div className="relative py-16 mb-24 lg:mb-28"> |
|||
<BackgroundSection className="bg-orange-50 dark:bg-black dark:bg-opacity-20 " /> |
|||
<SectionGridAuthorBox /> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
); |
|||
}; |
|||
|
|||
export default Layout; |
@ -1,14 +0,0 @@ |
|||
import React, { FC } from "react"; |
|||
import SectionGridHasMap from "../SectionGridHasMap"; |
|||
|
|||
export interface ListingExperiencesMapPageProps {} |
|||
|
|||
const ListingExperiencesMapPage: FC<ListingExperiencesMapPageProps> = ({}) => { |
|||
return ( |
|||
<div className="container pb-24 lg:pb-28 2xl:pl-10 xl:pr-0 xl:max-w-none"> |
|||
<SectionGridHasMap /> |
|||
</div> |
|||
); |
|||
}; |
|||
|
|||
export default ListingExperiencesMapPage; |
@ -1,14 +0,0 @@ |
|||
import React, { FC } from "react"; |
|||
import SectionGridFilterCard from "../SectionGridFilterCard"; |
|||
|
|||
export interface ListingExperiencesPageProps {} |
|||
|
|||
const ListingExperiencesPage: FC<ListingExperiencesPageProps> = ({}) => { |
|||
return ( |
|||
<div className="container relative"> |
|||
<SectionGridFilterCard className="pb-24 lg:pb-28" /> |
|||
</div> |
|||
); |
|||
}; |
|||
|
|||
export default ListingExperiencesPage; |
@ -1,195 +0,0 @@ |
|||
import SectionSliderNewCategories from "@/components/SectionSliderNewCategories"; |
|||
import React from "react"; |
|||
import SectionSubscribe2 from "@/components/SectionSubscribe2"; |
|||
import SectionOurFeatures from "@/components/SectionOurFeatures"; |
|||
import SectionHowItWork from "@/components/SectionHowItWork"; |
|||
import BackgroundSection from "@/components/BackgroundSection"; |
|||
import { TaxonomyType } from "@/data/types"; |
|||
import SectionGridAuthorBox from "@/components/SectionGridAuthorBox"; |
|||
//
|
|||
import logo1 from "@/images/logos/nomal/1.png"; |
|||
import logo1Dark from "@/images/logos/dark/1.png"; |
|||
//
|
|||
import logo2 from "@/images/logos/nomal/2.png"; |
|||
import logo2Dark from "@/images/logos/dark/2.png"; |
|||
//
|
|||
import logo3 from "@/images/logos/nomal/3.png"; |
|||
import logo3Dark from "@/images/logos/dark/3.png"; |
|||
//
|
|||
import logo4 from "@/images/logos/nomal/4.png"; |
|||
import logo4Dark from "@/images/logos/dark/4.png"; |
|||
//
|
|||
import logo5 from "@/images/logos/nomal/5.png"; |
|||
import logo5Dark from "@/images/logos/dark/5.png"; |
|||
//
|
|||
import HIW1img from "@/images/HIW2-1.png"; |
|||
import HIW2img from "@/images/HIW2-2.png"; |
|||
import HIW3img from "@/images/HIW2-3.png"; |
|||
import HIW1imgDark from "@/images/HIW2-1-dark.png"; |
|||
import HIW2imgDark from "@/images/HIW2-2-dark.png"; |
|||
import HIW3imgDark from "@/images/HIW2-3-dark.png"; |
|||
import rightImgPng from "@/images/our-features-2.png"; |
|||
|
|||
import SectionGridFeatureProperty from "../SectionGridFeatureProperty"; |
|||
import SectionDowloadApp from "../SectionDowloadApp"; |
|||
import SectionHero2 from "@/app/(server-components)/SectionHero2"; |
|||
import Image from "next/image"; |
|||
|
|||
const DEMO_CATS_2: TaxonomyType[] = [ |
|||
{ |
|||
id: "1", |
|||
href: "/listing-real-estate", |
|||
name: "Enjoy the great cold", |
|||
taxonomy: "category", |
|||
count: 188288, |
|||
thumbnail: |
|||
"https://images.pexels.com/photos/5764100/pexels-photo-5764100.jpeg?auto=compress&cs=tinysrgb&dpr=3&h=750&w=1260", |
|||
}, |
|||
{ |
|||
id: "2", |
|||
href: "/listing-real-estate", |
|||
name: "Sleep in a floating way", |
|||
taxonomy: "category", |
|||
count: 188288, |
|||
thumbnail: |
|||
"https://images.pexels.com/photos/2869499/pexels-photo-2869499.jpeg?auto=compress&cs=tinysrgb&dpr=2&h=750&w=1260", |
|||
}, |
|||
{ |
|||
id: "3", |
|||
href: "/listing-real-estate", |
|||
name: "In the billionaire's house", |
|||
taxonomy: "category", |
|||
count: 188288, |
|||
thumbnail: |
|||
"https://images.pexels.com/photos/7031413/pexels-photo-7031413.jpeg?auto=compress&cs=tinysrgb&dpr=2&h=750&w=1260", |
|||
}, |
|||
{ |
|||
id: "4", |
|||
href: "/listing-real-estate", |
|||
name: "Cool in the deep forest", |
|||
taxonomy: "category", |
|||
count: 188288, |
|||
thumbnail: |
|||
"https://images.pexels.com/photos/247532/pexels-photo-247532.jpeg?auto=compress&cs=tinysrgb&dpr=2&h=750&w=1260", |
|||
}, |
|||
{ |
|||
id: "5", |
|||
href: "/listing-real-estate", |
|||
name: "In the billionaire's house", |
|||
taxonomy: "category", |
|||
count: 188288, |
|||
thumbnail: |
|||
"https://images.pexels.com/photos/7031413/pexels-photo-7031413.jpeg?auto=compress&cs=tinysrgb&dpr=2&h=750&w=1260", |
|||
}, |
|||
{ |
|||
id: "6", |
|||
href: "/listing-real-estate", |
|||
name: "Sleep in a floating way", |
|||
taxonomy: "category", |
|||
count: 188288, |
|||
thumbnail: |
|||
"https://images.pexels.com/photos/2869499/pexels-photo-2869499.jpeg?auto=compress&cs=tinysrgb&dpr=2&h=750&w=1260", |
|||
}, |
|||
{ |
|||
id: "7", |
|||
href: "/listing-real-estate", |
|||
name: "In the billionaire's house", |
|||
taxonomy: "category", |
|||
count: 188288, |
|||
thumbnail: |
|||
"https://images.pexels.com/photos/7031413/pexels-photo-7031413.jpeg?auto=compress&cs=tinysrgb&dpr=2&h=750&w=1260", |
|||
}, |
|||
]; |
|||
|
|||
function PageHome2() { |
|||
return ( |
|||
<main className="nc-PageHome2 relative overflow-hidden"> |
|||
<div className="container relative space-y-24 mb-24 lg:space-y-28 lg:mb-28"> |
|||
<SectionHero2 className="" /> |
|||
|
|||
<div className="ncSectionLogos grid grid-cols-3 lg:grid-cols-5 gap-5 sm:gap-16"> |
|||
<div className="flex items-end justify-center"> |
|||
<Image className="block dark:hidden" src={logo1} alt="logo1" /> |
|||
<Image className="hidden dark:block" src={logo1Dark} alt="logo1" /> |
|||
</div> |
|||
<div className="flex items-end justify-center"> |
|||
<Image className="block dark:hidden" src={logo4} alt="logo4" /> |
|||
<Image className="hidden dark:block" src={logo4Dark} alt="logo4" /> |
|||
</div> |
|||
<div className="flex items-end justify-center"> |
|||
<Image className="block dark:hidden" src={logo2} alt="logo2" /> |
|||
<Image className="hidden dark:block" src={logo2Dark} alt="logo2" /> |
|||
</div> |
|||
<div className="flex items-end justify-center"> |
|||
<Image className="block dark:hidden" src={logo3} alt="logo3" /> |
|||
<Image className="hidden dark:block" src={logo3Dark} alt="logo3" /> |
|||
</div> |
|||
|
|||
<div className="flex items-end justify-center"> |
|||
<Image className="block dark:hidden" src={logo5} alt="logo5" /> |
|||
<Image className="hidden dark:block" src={logo5Dark} alt="logo5" /> |
|||
</div> |
|||
</div> |
|||
|
|||
<SectionHowItWork |
|||
data={[ |
|||
{ |
|||
id: 1, |
|||
img: HIW1img, |
|||
imgDark: HIW1imgDark, |
|||
title: "Smart search", |
|||
desc: "Name the area or type of home you are looking for the search bar. Our app will find you the perfect match.", |
|||
}, |
|||
{ |
|||
id: 2, |
|||
img: HIW2img, |
|||
imgDark: HIW2imgDark, |
|||
title: "Choose property", |
|||
desc: "From the number of options our app will provide, you can select any property that you like to explore.", |
|||
}, |
|||
{ |
|||
id: 3, |
|||
img: HIW3img, |
|||
imgDark: HIW3imgDark, |
|||
title: "Book you property", |
|||
desc: "Find a home or space from our search bar. Enter your specific location, property type and price range.", |
|||
}, |
|||
]} |
|||
/> |
|||
|
|||
<div className="relative py-16"> |
|||
<BackgroundSection /> |
|||
<SectionGridFeatureProperty /> |
|||
</div> |
|||
|
|||
<SectionOurFeatures type="type2" rightImg={rightImgPng} /> |
|||
|
|||
<SectionDowloadApp /> |
|||
|
|||
<SectionSliderNewCategories |
|||
categories={DEMO_CATS_2} |
|||
categoryCardType="card4" |
|||
itemPerRow={4} |
|||
heading="Suggestions for discovery" |
|||
subHeading="Popular places to stay that Chisfis recommends for you" |
|||
/> |
|||
|
|||
<div className="relative py-16"> |
|||
<BackgroundSection className="bg-neutral-100 dark:bg-black dark:bg-opacity-20 " /> |
|||
<SectionGridAuthorBox boxCard="box2" /> |
|||
</div> |
|||
|
|||
<SectionSliderNewCategories |
|||
heading="Explore by types of stays" |
|||
subHeading="Explore houses based on 10 types of stays" |
|||
categoryCardType="card5" |
|||
itemPerRow={5} |
|||
/> |
|||
|
|||
<SectionSubscribe2 /> |
|||
</div> |
|||
</main> |
|||
); |
|||
} |
|||
|
|||
export default PageHome2; |
@ -1,104 +0,0 @@ |
|||
import React from "react"; |
|||
import SectionSubscribe2 from "@/components/SectionSubscribe2"; |
|||
import BackgroundSection from "@/components/BackgroundSection"; |
|||
import BgGlassmorphism from "@/components/BgGlassmorphism"; |
|||
import { TaxonomyType } from "@/data/types"; |
|||
import SectionGridAuthorBox from "@/components/SectionGridAuthorBox"; |
|||
import SectionGridCategoryBox from "@/components/SectionGridCategoryBox"; |
|||
import SectionHero3 from "@/app/(server-components)/SectionHero3"; |
|||
import CardCategory6 from "@/components/CardCategory6"; |
|||
import SectionGridFeaturePlaces from "@/components/SectionGridFeaturePlaces"; |
|||
|
|||
const DEMO_CATS_2: TaxonomyType[] = [ |
|||
{ |
|||
id: "1", |
|||
href: "/listing-stay", |
|||
name: "Enjoy the great cold", |
|||
taxonomy: "category", |
|||
count: 188288, |
|||
thumbnail: |
|||
"https://images.pexels.com/photos/5764100/pexels-photo-5764100.jpeg?auto=compress&cs=tinysrgb&dpr=3&h=750&w=1260", |
|||
}, |
|||
{ |
|||
id: "222", |
|||
href: "/listing-stay", |
|||
name: "Sleep in a floating way", |
|||
taxonomy: "category", |
|||
count: 188288, |
|||
thumbnail: |
|||
"https://images.pexels.com/photos/2869499/pexels-photo-2869499.jpeg?auto=compress&cs=tinysrgb&dpr=2&h=750&w=1260", |
|||
}, |
|||
{ |
|||
id: "3", |
|||
href: "/listing-stay", |
|||
name: "In the billionaire's house", |
|||
taxonomy: "category", |
|||
count: 188288, |
|||
thumbnail: |
|||
"https://images.pexels.com/photos/7031413/pexels-photo-7031413.jpeg?auto=compress&cs=tinysrgb&dpr=2&h=750&w=1260", |
|||
}, |
|||
{ |
|||
id: "4", |
|||
href: "/listing-stay", |
|||
name: "Cool in the deep forest", |
|||
taxonomy: "category", |
|||
count: 188288, |
|||
thumbnail: |
|||
"https://images.pexels.com/photos/247532/pexels-photo-247532.jpeg?auto=compress&cs=tinysrgb&dpr=2&h=750&w=1260", |
|||
}, |
|||
{ |
|||
id: "5", |
|||
href: "/listing-stay", |
|||
name: "In the billionaire's house", |
|||
taxonomy: "category", |
|||
count: 188288, |
|||
thumbnail: |
|||
"https://images.pexels.com/photos/7031413/pexels-photo-7031413.jpeg?auto=compress&cs=tinysrgb&dpr=2&h=750&w=1260", |
|||
}, |
|||
]; |
|||
|
|||
function PageHome3() { |
|||
return ( |
|||
<main className="nc-PageHome3 relative overflow-hidden"> |
|||
{/* GLASSMOPHIN */} |
|||
<BgGlassmorphism /> |
|||
|
|||
{/* SECTION HERO */} |
|||
<div className="container px-1 sm:px-4 mb-24 "> |
|||
<SectionHero3 className="" /> |
|||
</div> |
|||
|
|||
<div className="container relative space-y-24 mb-24 "> |
|||
{/* SECTION 1 */} |
|||
<div className="grid grid-cols-12 gap-6"> |
|||
<div className="col-span-12 sm:col-span-6 lg:col-span-4 flex"> |
|||
<CardCategory6 taxonomy={DEMO_CATS_2[0]} /> |
|||
</div> |
|||
<div className="col-span-12 sm:col-span-6 lg:col-span-4 grid grid-rows-2 gap-6"> |
|||
<CardCategory6 taxonomy={DEMO_CATS_2[3]} /> |
|||
<CardCategory6 taxonomy={DEMO_CATS_2[1]} /> |
|||
</div> |
|||
<div className="col-span-12 sm:col-span-6 lg:col-span-4 flex"> |
|||
<CardCategory6 taxonomy={DEMO_CATS_2[4]} /> |
|||
</div> |
|||
</div> |
|||
|
|||
{/* SECTION */} |
|||
<SectionGridCategoryBox /> |
|||
|
|||
{/* SECTION */} |
|||
<div className="relative py-16"> |
|||
<BackgroundSection /> |
|||
<SectionGridAuthorBox boxCard="box2" /> |
|||
</div> |
|||
|
|||
<SectionGridFeaturePlaces /> |
|||
|
|||
{/* SECTION */} |
|||
<SectionSubscribe2 /> |
|||
</div> |
|||
</main> |
|||
); |
|||
} |
|||
|
|||
export default PageHome3; |
@ -1,55 +0,0 @@ |
|||
import React, { FC, Fragment, useState } from "react"; |
|||
import DatePicker from "react-datepicker"; |
|||
import DatePickerCustomHeaderTwoMonth from "@/components/DatePickerCustomHeaderTwoMonth"; |
|||
import DatePickerCustomDay from "@/components/DatePickerCustomDay"; |
|||
|
|||
const SectionDateRange = () => { |
|||
const [startDate, setStartDate] = useState<Date | null>( |
|||
new Date("2023/02/06") |
|||
); |
|||
const [endDate, setEndDate] = useState<Date | null>(new Date("2023/02/23")); |
|||
const onChangeDate = (dates: [Date | null, Date | null]) => { |
|||
const [start, end] = dates; |
|||
setStartDate(start); |
|||
setEndDate(end); |
|||
}; |
|||
|
|||
const renderSectionCheckIndate = () => { |
|||
return ( |
|||
<div className="listingSection__wrap overflow-hidden"> |
|||
{/* HEADING */} |
|||
<div> |
|||
<h2 className="text-2xl font-semibold">Availability</h2> |
|||
<span className="block mt-2 text-neutral-500 dark:text-neutral-400"> |
|||
Prices may increase on weekends or holidays |
|||
</span> |
|||
</div> |
|||
<div className="w-14 border-b border-neutral-200 dark:border-neutral-700"></div> |
|||
{/* CONTENT */} |
|||
|
|||
<div className=""> |
|||
<DatePicker |
|||
selected={startDate} |
|||
onChange={onChangeDate} |
|||
startDate={startDate} |
|||
endDate={endDate} |
|||
selectsRange |
|||
monthsShown={2} |
|||
showPopperArrow={false} |
|||
inline |
|||
renderCustomHeader={(p) => ( |
|||
<DatePickerCustomHeaderTwoMonth {...p} /> |
|||
)} |
|||
renderDayContents={(day, date) => ( |
|||
<DatePickerCustomDay dayOfMonth={day} date={date} /> |
|||
)} |
|||
/> |
|||
</div> |
|||
</div> |
|||
); |
|||
}; |
|||
|
|||
// return renderSectionCheckIndate();
|
|||
}; |
|||
|
|||
export default SectionDateRange; |
@ -1,77 +0,0 @@ |
|||
"use client"; |
|||
|
|||
import BackgroundSection from "@/components/BackgroundSection"; |
|||
import ListingImageGallery from "@/components/listing-image-gallery/ListingImageGallery"; |
|||
import SectionSliderNewCategories from "@/components/SectionSliderNewCategories"; |
|||
import SectionSubscribe2 from "@/components/SectionSubscribe2"; |
|||
import { usePathname, useRouter, useSearchParams } from "next/navigation"; |
|||
import React, { ReactNode, useEffect, useState } from "react"; |
|||
import MobileFooterSticky from "./(components)/MobileFooterSticky"; |
|||
import { imageGallery as listingStayImageGallery } from "./listing-stay-detail/constant"; |
|||
import { imageGallery as listingCarImageGallery } from "./listing-car-detail/constant"; |
|||
import { imageGallery as listingExperienceImageGallery } from "./listing-experiences-detail/constant"; |
|||
import { Route } from "next"; |
|||
import axiosInstance from "@/components/api/axios"; |
|||
import axios from "axios"; |
|||
|
|||
const DetailtLayout = ({ children }: { children: ReactNode }) => { |
|||
const router = useRouter(); |
|||
const thisPathname = usePathname(); |
|||
const searchParams = useSearchParams(); |
|||
const modal = searchParams?.get("modal"); |
|||
|
|||
const handleCloseModalImageGallery = () => { |
|||
let params = new URLSearchParams(document.location.search); |
|||
params.delete("modal"); |
|||
router.push(`${thisPathname}/?${params.toString()}` as Route); |
|||
}; |
|||
|
|||
const getImageGalleryListing = () => { |
|||
|
|||
|
|||
if (thisPathname?.includes("/listing-stay-detail")) { |
|||
return listingStayImageGallery; |
|||
} |
|||
if (thisPathname?.includes("/listing-car-detail")) { |
|||
return listingCarImageGallery; |
|||
} |
|||
if (thisPathname?.includes("/listing-experiences-detail")) { |
|||
return listingExperienceImageGallery; |
|||
} |
|||
|
|||
return []; |
|||
}; |
|||
|
|||
|
|||
return ( |
|||
<div className="ListingDetailPage"> |
|||
<ListingImageGallery |
|||
isShowModal={modal === "PHOTO_TOUR_SCROLLABLE"} |
|||
onClose={handleCloseModalImageGallery} |
|||
images={getImageGalleryListing()} |
|||
/> |
|||
|
|||
<div className="container ListingDetailPage__content">{children}</div> |
|||
|
|||
{/* OTHER SECTION */} |
|||
<div className="container py-24 lg:py-32"> |
|||
<div className="relative py-16"> |
|||
<BackgroundSection /> |
|||
<SectionSliderNewCategories |
|||
heading="Explore by types of stays" |
|||
subHeading="Explore houses based on 10 types of stays" |
|||
categoryCardType="card5" |
|||
itemPerRow={5} |
|||
sliderStyle="style2" |
|||
/> |
|||
</div> |
|||
<SectionSubscribe2 className="pt-24 lg:pt-32" /> |
|||
</div> |
|||
|
|||
{/* STICKY FOOTER MOBILE */} |
|||
<MobileFooterSticky /> |
|||
</div> |
|||
); |
|||
}; |
|||
|
|||
export default DetailtLayout; |
@ -1,118 +0,0 @@ |
|||
"use client"; |
|||
|
|||
import React, { Fragment, useState } from "react"; |
|||
import { FC } from "react"; |
|||
import DatePicker from "react-datepicker"; |
|||
import { Popover, Transition } from "@headlessui/react"; |
|||
import { CalendarIcon } from "@heroicons/react/24/outline"; |
|||
import DatePickerCustomHeaderTwoMonth from "@/components/DatePickerCustomHeaderTwoMonth"; |
|||
import DatePickerCustomDay from "@/components/DatePickerCustomDay"; |
|||
import ClearDataButton from "@/app/(client-components)/(HeroSearchForm)/ClearDataButton"; |
|||
|
|||
export interface RentalCarDatesRangeInputProps { |
|||
className?: string; |
|||
} |
|||
|
|||
const RentalCarDatesRangeInput: FC<RentalCarDatesRangeInputProps> = ({ |
|||
className = "", |
|||
}) => { |
|||
const [startDate, setStartDate] = useState<Date | null>( |
|||
new Date("2023/03/01") |
|||
); |
|||
const [endDate, setEndDate] = useState<Date | null>(new Date("2023/03/16")); |
|||
|
|||
const onChangeDate = (dates: [Date | null, Date | null]) => { |
|||
const [start, end] = dates; |
|||
setStartDate(start); |
|||
setEndDate(end); |
|||
}; |
|||
|
|||
const renderInput = () => { |
|||
return ( |
|||
<> |
|||
<div className="text-neutral-300 dark:text-neutral-400"> |
|||
<CalendarIcon className="w-5 h-5 lg:w-7 lg:h-7" /> |
|||
</div> |
|||
<div className="flex-grow text-left"> |
|||
<span className="block xl:text-lg font-semibold"> |
|||
{startDate?.toLocaleDateString("en-US", { |
|||
month: "short", |
|||
day: "2-digit", |
|||
}) || "Add dates"} |
|||
{endDate |
|||
? " - " + |
|||
endDate?.toLocaleDateString("en-US", { |
|||
month: "short", |
|||
day: "2-digit", |
|||
}) |
|||
: ""} |
|||
</span> |
|||
<span className="block mt-1 text-sm text-neutral-400 leading-none font-light"> |
|||
{"Pick up - Drop off"} |
|||
</span> |
|||
</div> |
|||
</> |
|||
); |
|||
}; |
|||
|
|||
return ( |
|||
<> |
|||
<Popover |
|||
className={`RentalCarDatesRangeInput relative flex w-full ${className}`} |
|||
> |
|||
{({ open }) => ( |
|||
<> |
|||
<div |
|||
className={`flex-1 flex items-center focus:outline-none rounded-2xl ${ |
|||
open ? "shadow-lg" : "" |
|||
}`}
|
|||
> |
|||
<Popover.Button |
|||
className={`flex-1 flex relative p-3 items-center space-x-3 focus:outline-none `} |
|||
> |
|||
{renderInput()} |
|||
|
|||
{startDate && open && ( |
|||
<ClearDataButton onClick={() => onChangeDate([null, null])} /> |
|||
)} |
|||
</Popover.Button> |
|||
</div> |
|||
|
|||
<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 mt-3 top-full right-0 xl:-right-10 w-screen max-w-sm px-4 sm:px-0 lg:max-w-3xl"> |
|||
<div className="overflow-hidden rounded-3xl shadow-lg ring-1 ring-black ring-opacity-5 bg-white dark:bg-neutral-800 p-8"> |
|||
<DatePicker |
|||
selected={startDate} |
|||
onChange={onChangeDate} |
|||
startDate={startDate} |
|||
endDate={endDate} |
|||
selectsRange |
|||
monthsShown={2} |
|||
showPopperArrow={false} |
|||
inline |
|||
renderCustomHeader={(p) => ( |
|||
<DatePickerCustomHeaderTwoMonth {...p} /> |
|||
)} |
|||
renderDayContents={(day, date) => ( |
|||
<DatePickerCustomDay dayOfMonth={day} date={date} /> |
|||
)} |
|||
/> |
|||
</div> |
|||
</Popover.Panel> |
|||
</Transition> |
|||
</> |
|||
)} |
|||
</Popover> |
|||
</> |
|||
); |
|||
}; |
|||
|
|||
export default RentalCarDatesRangeInput; |
@ -1,68 +0,0 @@ |
|||
import { ListingGalleryImage } from "@/components/listing-image-gallery/utils/types"; |
|||
import carUtilities1 from "@/images/carUtilities/1.png"; |
|||
import carUtilities2 from "@/images/carUtilities/2.png"; |
|||
import carUtilities3 from "@/images/carUtilities/3.png"; |
|||
import carUtilities4 from "@/images/carUtilities/4.png"; |
|||
import carUtilities5 from "@/images/carUtilities/5.png"; |
|||
import carUtilities6 from "@/images/carUtilities/6.png"; |
|||
import carUtilities7 from "@/images/carUtilities/7.png"; |
|||
import carUtilities8 from "@/images/carUtilities/8.png"; |
|||
|
|||
export const PHOTOS: string[] = [ |
|||
"https://images.pexels.com/photos/381292/pexels-photo-381292.jpeg?auto=compress&cs=tinysrgb&dpr=2&h=750&w=1260", |
|||
"https://images.pexels.com/photos/2526128/pexels-photo-2526128.jpeg?auto=compress&cs=tinysrgb&dpr=2&h=750&w=1260", |
|||
"https://images.pexels.com/photos/2827753/pexels-photo-2827753.jpeg?auto=compress&cs=tinysrgb&dpr=2&h=750&w=1260", |
|||
"https://images.pexels.com/photos/1637859/pexels-photo-1637859.jpeg?auto=compress&cs=tinysrgb&dpr=2&h=750&w=1260", |
|||
"https://images.pexels.com/photos/257851/pexels-photo-257851.jpeg?auto=compress&cs=tinysrgb&dpr=2&h=750&w=1260", |
|||
"https://images.pexels.com/photos/457418/pexels-photo-457418.jpeg?auto=compress&cs=tinysrgb&w=1600", |
|||
"https://images.pexels.com/photos/1707820/pexels-photo-1707820.jpeg?auto=compress&cs=tinysrgb&w=1600", |
|||
"https://images.pexels.com/photos/712618/pexels-photo-712618.jpeg?auto=compress&cs=tinysrgb&w=1600", |
|||
"https://images.pexels.com/photos/3802508/pexels-photo-3802508.jpeg?auto=compress&cs=tinysrgb&w=1600", |
|||
"https://images.pexels.com/photos/945443/pexels-photo-945443.jpeg?auto=compress&cs=tinysrgb&w=1600", |
|||
"https://images.pexels.com/photos/1054211/pexels-photo-1054211.jpeg?auto=compress&cs=tinysrgb&w=1600", |
|||
"https://images.pexels.com/photos/189454/pexels-photo-189454.jpeg?auto=compress&cs=tinysrgb&dpr=2&h=750&w=1260", |
|||
"https://images.pexels.com/photos/193995/pexels-photo-193995.jpeg?auto=compress&cs=tinysrgb&dpr=2&h=750&w=1260", |
|||
"https://images.pexels.com/photos/575386/pexels-photo-575386.jpeg?auto=compress&cs=tinysrgb&dpr=2&h=750&w=1260", |
|||
"https://images.pexels.com/photos/248687/pexels-photo-248687.jpeg?auto=compress&cs=tinysrgb&w=1600", |
|||
"https://images.pexels.com/photos/326259/pexels-photo-326259.jpeg?auto=compress&cs=tinysrgb&w=1600", |
|||
"https://images.pexels.com/photos/2127733/pexels-photo-2127733.jpeg?auto=compress&cs=tinysrgb&w=1600", |
|||
"https://images.pexels.com/photos/2882234/pexels-photo-2882234.jpeg?auto=compress&cs=tinysrgb&w=1600", |
|||
"https://images.pexels.com/photos/752615/pexels-photo-752615.jpeg?auto=compress&cs=tinysrgb&w=1600", |
|||
"https://images.pexels.com/photos/1210622/pexels-photo-1210622.jpeg?auto=compress&cs=tinysrgb&w=1600", |
|||
"https://images.pexels.com/photos/303316/pexels-photo-303316.jpeg?auto=compress&cs=tinysrgb&w=1600", |
|||
"https://images.pexels.com/photos/136872/pexels-photo-136872.jpeg?auto=compress&cs=tinysrgb&w=1600", |
|||
]; |
|||
|
|||
export const includes_demo = [ |
|||
{ name: "Free cancellation up to 48 hours before pick-up" }, |
|||
{ name: "Collision Damage Waiver with $214 deductible" }, |
|||
{ name: "Theft Protection with $19,999 excess" }, |
|||
{ name: "Unlimited mileage" }, |
|||
{ |
|||
name: "Car interiors and exteriors cleaned with disinfectant before pick-up", |
|||
}, |
|||
{ name: "Masks are required at the pick-up location" }, |
|||
]; |
|||
|
|||
export const Amenities_demos = [ |
|||
{ name: "59 MPG Combined, 58 City/60 Hwy", icon: carUtilities1 }, |
|||
{ |
|||
name: "Forward Collision-Avoidance Assist with Pedestrian Detection (FCA-Ped)", |
|||
icon: carUtilities2, |
|||
}, |
|||
{ name: "139-hp gas/electric combined", icon: carUtilities3 }, |
|||
{ name: "Proximity Key with push button start", icon: carUtilities4 }, |
|||
{ name: "8-inch color touchscreen display audio", icon: carUtilities5 }, |
|||
{ name: "Smart Cruise Control with Stop & Go (SCC)", icon: carUtilities6 }, |
|||
{ name: "LED Daytime Running Lights (DRL)", icon: carUtilities7 }, |
|||
{ name: "Blind-Spot Collision Warning (BCW)", icon: carUtilities8 }, |
|||
]; |
|||
|
|||
export const imageGallery: ListingGalleryImage[] = [...PHOTOS].map( |
|||
(item, index): ListingGalleryImage => { |
|||
return { |
|||
id: index, |
|||
url: item, |
|||
}; |
|||
} |
|||
); |
@ -1,542 +0,0 @@ |
|||
"use client"; |
|||
|
|||
import React, { FC, useState } from "react"; |
|||
import { ArrowRightIcon, Squares2X2Icon } from "@heroicons/react/24/outline"; |
|||
import CommentListing from "@/components/CommentListing"; |
|||
import FiveStartIconForRate from "@/components/FiveStartIconForRate"; |
|||
import StartRating from "@/components/StartRating"; |
|||
import Avatar from "@/shared/Avatar"; |
|||
import Badge from "@/shared/Badge"; |
|||
import ButtonCircle from "@/shared/ButtonCircle"; |
|||
import ButtonPrimary from "@/shared/ButtonPrimary"; |
|||
import ButtonSecondary from "@/shared/ButtonSecondary"; |
|||
import Input from "@/shared/Input"; |
|||
import Image from "next/image"; |
|||
import { Amenities_demos, includes_demo, PHOTOS } from "./constant"; |
|||
import LikeSaveBtns from "@/components/LikeSaveBtns"; |
|||
import { usePathname, useRouter } from "next/navigation"; |
|||
import SectionDateRange from "../SectionDateRange"; |
|||
import RentalCarDatesRangeInput from "./RentalCarDatesRangeInput"; |
|||
import { Route } from "next"; |
|||
|
|||
export interface ListingCarDetailPageProps {} |
|||
|
|||
const ListingCarDetailPage: FC<ListingCarDetailPageProps> = ({}) => { |
|||
// USE STATE
|
|||
|
|||
const thisPathname = usePathname(); |
|||
const router = useRouter(); |
|||
|
|||
const handleOpenModalImageGallery = () => { |
|||
router.push(`${thisPathname}/?modal=PHOTO_TOUR_SCROLLABLE` as Route); |
|||
}; |
|||
|
|||
const renderSection1 = () => { |
|||
return ( |
|||
<div className="listingSection__wrap !space-y-6"> |
|||
{/* 1 */} |
|||
<div className="flex justify-between items-center"> |
|||
<Badge color="pink" name="BMW car" /> |
|||
<LikeSaveBtns /> |
|||
</div> |
|||
|
|||
{/* 2 */} |
|||
<h2 className="text-2xl sm:text-3xl lg:text-4xl font-semibold"> |
|||
BMW 3 Series Sedan |
|||
</h2> |
|||
|
|||
{/* 3 */} |
|||
<div className="flex items-center space-x-4"> |
|||
<StartRating /> |
|||
<span>·</span> |
|||
<span> |
|||
<i className="las la-map-marker-alt"></i> |
|||
<span className="ml-1"> Tokyo, Jappan</span> |
|||
</span> |
|||
</div> |
|||
|
|||
{/* 4 */} |
|||
<div className="flex items-center"> |
|||
<Avatar hasChecked sizeClass="h-10 w-10" radius="rounded-full" /> |
|||
<span className="ml-2.5 text-neutral-500 dark:text-neutral-400"> |
|||
Car owner{" "} |
|||
<span className="text-neutral-900 dark:text-neutral-200 font-medium"> |
|||
Kevin Francis |
|||
</span> |
|||
</span> |
|||
</div> |
|||
|
|||
{/* 5 */} |
|||
<div className="w-full border-b border-neutral-100 dark:border-neutral-700" /> |
|||
|
|||
{/* 6 */} |
|||
<div className="flex items-center justify-between xl:justify-start space-x-8 xl:space-x-12 text-sm text-neutral-700 dark:text-neutral-300"> |
|||
<div className="flex flex-col sm:flex-row items-center space-y-3 sm:space-y-0 text-center sm:text-left sm:space-x-3 "> |
|||
<i className="las la-user-friends text-2xl"></i> |
|||
<span className="">4 seats</span> |
|||
</div> |
|||
<div className="flex flex-col sm:flex-row items-center space-y-3 sm:space-y-0 text-center sm:text-left sm:space-x-3 "> |
|||
<i className="las la-dharmachakra text-2xl"></i> |
|||
<span className=""> Auto gearbox</span> |
|||
</div> |
|||
<div className="flex flex-col sm:flex-row items-center space-y-3 sm:space-y-0 text-center sm:text-left sm:space-x-3 "> |
|||
<i className="las la-suitcase text-2xl"></i> |
|||
<span className=""> 2 bags</span> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
); |
|||
}; |
|||
|
|||
//
|
|||
const renderSectionTienIch = () => { |
|||
return ( |
|||
<div className="listingSection__wrap"> |
|||
<div> |
|||
<h2 className="text-2xl font-semibold"> |
|||
Vehicle parameters & utilities{" "} |
|||
</h2> |
|||
<span className="block mt-2 text-neutral-500 dark:text-neutral-400"> |
|||
Questions are at the heart of making things great. |
|||
</span> |
|||
</div> |
|||
<div className="w-14 border-b border-neutral-200 dark:border-neutral-700"></div> |
|||
{/* 6 */} |
|||
<div className="grid grid-cols-1 lg:grid-cols-2 gap-y-6 gap-x-10 text-sm text-neutral-700 dark:text-neutral-300 "> |
|||
{/* TIEN ICH 1 */} |
|||
{Amenities_demos.map((item, index) => ( |
|||
<div key={index} className="flex items-center space-x-4 "> |
|||
<div className="w-10 flex-shrink-0"> |
|||
<Image src={item.icon} alt="" /> |
|||
</div> |
|||
<span>{item.name}</span> |
|||
</div> |
|||
))} |
|||
</div> |
|||
</div> |
|||
); |
|||
}; |
|||
|
|||
const renderSection2 = () => { |
|||
return ( |
|||
<div className="listingSection__wrap"> |
|||
<h2 className="text-2xl font-semibold">Car descriptions</h2> |
|||
<div className="w-14 border-b border-neutral-200 dark:border-neutral-700"></div> |
|||
<div className="text-neutral-6000 dark:text-neutral-300"> |
|||
<p> |
|||
Until the all-new TUCSON hits the dealer showrooms you can check it |
|||
out in our Showroom Walkaround video. Watch the video and join our |
|||
product specialist as he gives you an up-close look of our latest |
|||
SUV |
|||
<br /> |
|||
<br /> |
|||
Questions are at the heart of making things great. Watch our |
|||
celebrity-filled TV ad and you’ll see that when we say “everything,” |
|||
we mean everything. |
|||
</p> |
|||
</div> |
|||
</div> |
|||
); |
|||
}; |
|||
|
|||
const renderSection3 = () => { |
|||
return ( |
|||
<div className="listingSection__wrap"> |
|||
<div> |
|||
<h2 className="text-2xl font-semibold">Include </h2> |
|||
<span className="block mt-2 text-neutral-500 dark:text-neutral-400"> |
|||
Included in the price |
|||
</span> |
|||
</div> |
|||
<div className="w-14 border-b border-neutral-200 dark:border-neutral-700"></div> |
|||
{/* 6 */} |
|||
<div className="grid grid-cols-1 lg:grid-cols-2 gap-6 text-sm text-neutral-700 dark:text-neutral-300 "> |
|||
{includes_demo |
|||
.filter((_, i) => i < 12) |
|||
.map((item) => ( |
|||
<div key={item.name} className="flex items-center space-x-3"> |
|||
<i className="las la-check-circle text-2xl"></i> |
|||
<span>{item.name}</span> |
|||
</div> |
|||
))} |
|||
</div> |
|||
</div> |
|||
); |
|||
}; |
|||
|
|||
const renderSection5 = () => { |
|||
return ( |
|||
<div className="listingSection__wrap"> |
|||
{/* HEADING */} |
|||
<h2 className="text-2xl font-semibold">Car Owner</h2> |
|||
<div className="w-14 border-b border-neutral-200 dark:border-neutral-700"></div> |
|||
|
|||
{/* host */} |
|||
<div className="flex items-center space-x-4"> |
|||
<Avatar |
|||
hasChecked |
|||
hasCheckedClass="w-4 h-4 -top-0.5 right-0.5" |
|||
sizeClass="h-14 w-14" |
|||
radius="rounded-full" |
|||
/> |
|||
<div> |
|||
<a className="block text-xl font-medium" href="##"> |
|||
Kevin Francis |
|||
</a> |
|||
<div className="mt-1.5 flex items-center text-sm text-neutral-500 dark:text-neutral-400"> |
|||
<StartRating /> |
|||
<span className="mx-2">·</span> |
|||
<span> 12 places</span> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
|
|||
{/* desc */} |
|||
<span className="block text-neutral-6000 dark:text-neutral-300"> |
|||
Providing lake views, The Symphony 9 Tam Coc in Ninh Binh provides |
|||
accommodation, an outdoor swimming pool, a bar, a shared lounge, a |
|||
garden and barbecue facilities... |
|||
</span> |
|||
|
|||
{/* info */} |
|||
<div className="block text-neutral-500 dark:text-neutral-400 space-y-2.5"> |
|||
<div className="flex items-center space-x-3"> |
|||
<svg |
|||
xmlns="http://www.w3.org/2000/svg" |
|||
className="h-6 w-6" |
|||
fill="none" |
|||
viewBox="0 0 24 24" |
|||
stroke="currentColor" |
|||
> |
|||
<path |
|||
strokeLinecap="round" |
|||
strokeLinejoin="round" |
|||
strokeWidth={1.5} |
|||
d="M8 7V3m8 4V3m-9 8h10M5 21h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v12a2 2 0 002 2z" |
|||
/> |
|||
</svg> |
|||
<span>Joined in March 2016</span> |
|||
</div> |
|||
<div className="flex items-center space-x-3"> |
|||
<svg |
|||
xmlns="http://www.w3.org/2000/svg" |
|||
className="h-6 w-6" |
|||
fill="none" |
|||
viewBox="0 0 24 24" |
|||
stroke="currentColor" |
|||
> |
|||
<path |
|||
strokeLinecap="round" |
|||
strokeLinejoin="round" |
|||
strokeWidth={1.5} |
|||
d="M7 8h10M7 12h4m1 8l-4-4H5a2 2 0 01-2-2V6a2 2 0 012-2h14a2 2 0 012 2v8a2 2 0 01-2 2h-3l-4 4z" |
|||
/> |
|||
</svg> |
|||
<span>Response rate - 100%</span> |
|||
</div> |
|||
<div className="flex items-center space-x-3"> |
|||
<svg |
|||
xmlns="http://www.w3.org/2000/svg" |
|||
className="h-6 w-6" |
|||
fill="none" |
|||
viewBox="0 0 24 24" |
|||
stroke="currentColor" |
|||
> |
|||
<path |
|||
strokeLinecap="round" |
|||
strokeLinejoin="round" |
|||
strokeWidth={1.5} |
|||
d="M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z" |
|||
/> |
|||
</svg> |
|||
|
|||
<span>Fast response - within a few hours</span> |
|||
</div> |
|||
</div> |
|||
|
|||
{/* == */} |
|||
<div className="w-14 border-b border-neutral-200 dark:border-neutral-700"></div> |
|||
<div> |
|||
<ButtonSecondary href="/author">See host profile</ButtonSecondary> |
|||
</div> |
|||
</div> |
|||
); |
|||
}; |
|||
|
|||
const renderSection6 = () => { |
|||
return ( |
|||
<div className="listingSection__wrap"> |
|||
{/* HEADING */} |
|||
<h2 className="text-2xl font-semibold">Reviews (23 reviews)</h2> |
|||
<div className="w-14 border-b border-neutral-200 dark:border-neutral-700"></div> |
|||
|
|||
{/* Content */} |
|||
<div className="space-y-5"> |
|||
<FiveStartIconForRate iconClass="w-6 h-6" className="space-x-0.5" /> |
|||
<div className="relative"> |
|||
<Input |
|||
fontClass="" |
|||
sizeClass="h-16 px-4 py-3" |
|||
rounded="rounded-3xl" |
|||
placeholder="Share your thoughts ..." |
|||
/> |
|||
<ButtonCircle |
|||
className="absolute right-2 top-1/2 transform -translate-y-1/2" |
|||
size=" w-12 h-12 " |
|||
> |
|||
<ArrowRightIcon className="w-5 h-5" /> |
|||
</ButtonCircle> |
|||
</div> |
|||
</div> |
|||
|
|||
{/* comment */} |
|||
<div className="divide-y divide-neutral-100 dark:divide-neutral-800"> |
|||
<CommentListing className="py-8" /> |
|||
<CommentListing className="py-8" /> |
|||
<CommentListing className="py-8" /> |
|||
<CommentListing className="py-8" /> |
|||
<div className="pt-8"> |
|||
<ButtonSecondary>View more 20 reviews</ButtonSecondary> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
); |
|||
}; |
|||
|
|||
const renderSection7 = () => { |
|||
return ( |
|||
<div className="listingSection__wrap"> |
|||
{/* HEADING */} |
|||
<div> |
|||
<h2 className="text-2xl font-semibold">Location</h2> |
|||
<span className="block mt-2 text-neutral-500 dark:text-neutral-400"> |
|||
San Diego, CA, United States of America (SAN-San Diego Intl.) |
|||
</span> |
|||
</div> |
|||
<div className="w-14 border-b border-neutral-200 dark:border-neutral-700" /> |
|||
|
|||
{/* MAP */} |
|||
<div className="aspect-w-5 aspect-h-5 sm:aspect-h-3 ring-1 ring-black/10 rounded-xl z-0"> |
|||
<div className="rounded-xl overflow-hidden z-0"> |
|||
<iframe |
|||
width="100%" |
|||
height="100%" |
|||
loading="lazy" |
|||
allowFullScreen |
|||
referrerPolicy="no-referrer-when-downgrade" |
|||
src="https://www.google.com/maps/embed/v1/place?key=AIzaSyAGVJfZMAKYfZ71nzL_v5i3LjTTWnCYwTY&q=Eiffel+Tower,Paris+France" |
|||
></iframe> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
); |
|||
}; |
|||
|
|||
const renderSection8 = () => { |
|||
return ( |
|||
<div className="listingSection__wrap"> |
|||
{/* HEADING */} |
|||
<h2 className="text-2xl font-semibold">Things to know</h2> |
|||
<div className="w-14 border-b border-neutral-200 dark:border-neutral-700" /> |
|||
|
|||
{/* CONTENT */} |
|||
<div> |
|||
<h4 className="text-lg font-semibold">Cancellation policy</h4> |
|||
<span className="block mt-3 text-neutral-500 dark:text-neutral-400"> |
|||
Lock in this fantastic price today, cancel free of charge anytime. |
|||
Reserve now and pay at pick-up. |
|||
</span> |
|||
</div> |
|||
<div className="w-14 border-b border-neutral-200 dark:border-neutral-700" /> |
|||
|
|||
{/* CONTENT */} |
|||
<div> |
|||
<h4 className="text-lg font-semibold">Special Note</h4> |
|||
<span className="block mt-3 text-neutral-500 dark:text-neutral-400"> |
|||
We asked ourselves, “How can we make the dash not only look better, |
|||
but also give the driver a better look outside?” The unexpected |
|||
answer is having no hood above the available 10.25-inch digital |
|||
instrument cluster... |
|||
</span> |
|||
</div> |
|||
</div> |
|||
); |
|||
}; |
|||
|
|||
const renderSidebarPrice = () => { |
|||
return ( |
|||
<div className="listingSectionSidebar__wrap shadow-xl"> |
|||
{/* PRICE */} |
|||
<div className="flex justify-between"> |
|||
<span className="text-3xl font-semibold"> |
|||
$19 |
|||
<span className="ml-1 text-base font-normal text-neutral-500 dark:text-neutral-400"> |
|||
/day |
|||
</span> |
|||
</span> |
|||
<StartRating /> |
|||
</div> |
|||
|
|||
{/* FORM */} |
|||
<form className="border border-neutral-200 dark:border-neutral-700 rounded-2xl"> |
|||
<RentalCarDatesRangeInput /> |
|||
</form> |
|||
|
|||
{/* SUM */} |
|||
<div className="flex flex-col space-y-4 "> |
|||
<div className="flex justify-between text-neutral-6000 dark:text-neutral-300"> |
|||
<span>$19 x 3 day</span> |
|||
<span>$57</span> |
|||
</div> |
|||
|
|||
<div className="border-b border-neutral-200 dark:border-neutral-700"></div> |
|||
<div className="flex justify-between font-semibold"> |
|||
<span>Total</span> |
|||
<span>$199</span> |
|||
</div> |
|||
</div> |
|||
|
|||
{/* SUBMIT */} |
|||
<ButtonPrimary href={"/checkout"}>Reserve</ButtonPrimary> |
|||
</div> |
|||
); |
|||
}; |
|||
|
|||
const renderSidebarDetail = () => { |
|||
return ( |
|||
<div className="listingSection__wrap lg:shadow-xl"> |
|||
<span className="text-2xl font-semibold block"> |
|||
Pick up and drop off |
|||
</span> |
|||
<div className="mt-8 flex"> |
|||
<div className="flex-shrink-0 flex flex-col items-center py-2"> |
|||
<span className="block w-6 h-6 rounded-full border border-neutral-400"></span> |
|||
<span className="block flex-grow border-l border-neutral-400 border-dashed my-1"></span> |
|||
<span className="block w-6 h-6 rounded-full border border-neutral-400"></span> |
|||
</div> |
|||
<div className="ml-4 space-y-14 text-sm"> |
|||
<div className="flex flex-col space-y-2"> |
|||
<span className=" text-neutral-500 dark:text-neutral-400"> |
|||
Monday, August 12 · 10:00 |
|||
</span> |
|||
<span className=" font-semibold"> |
|||
Saint Petersburg City Center |
|||
</span> |
|||
</div> |
|||
<div className="flex flex-col space-y-2"> |
|||
<span className=" text-neutral-500 dark:text-neutral-400"> |
|||
Monday, August 16 · 10:00 |
|||
</span> |
|||
<span className=" font-semibold"> |
|||
Saint Petersburg City Center |
|||
</span> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
); |
|||
}; |
|||
|
|||
return ( |
|||
<div className={` nc-ListingCarDetailPage `}> |
|||
{/* SINGLE HEADER */} |
|||
<header className="rounded-md sm:rounded-xl"> |
|||
<div className="relative grid grid-cols-4 gap-1 sm:gap-2"> |
|||
<div |
|||
className="col-span-2 row-span-2 relative rounded-md sm:rounded-xl overflow-hidden cursor-pointer" |
|||
onClick={handleOpenModalImageGallery} |
|||
> |
|||
<Image |
|||
fill |
|||
src={PHOTOS[0]} |
|||
alt="photo 0" |
|||
className="object-cover rounded-md sm:rounded-xl" |
|||
sizes="(max-width: 768px) 100vw, (max-width: 1200px) 50vw, 50vw" |
|||
/> |
|||
<div className="absolute inset-0 bg-neutral-900 bg-opacity-20 opacity-0 hover:opacity-100 transition-opacity"></div> |
|||
</div> |
|||
|
|||
{/* */} |
|||
<div |
|||
className="col-span-1 row-span-2 relative rounded-md sm:rounded-xl overflow-hidden cursor-pointer" |
|||
onClick={handleOpenModalImageGallery} |
|||
> |
|||
<Image |
|||
fill |
|||
className="object-cover rounded-md sm:rounded-xl" |
|||
src={PHOTOS[1]} |
|||
alt="photo 1" |
|||
sizes="400px" |
|||
/> |
|||
<div className="absolute inset-0 bg-neutral-900 bg-opacity-20 opacity-0 hover:opacity-100 transition-opacity"></div> |
|||
</div> |
|||
|
|||
{/* */} |
|||
{PHOTOS.filter((_, i) => i >= 2 && i < 4).map((item, index) => ( |
|||
<div |
|||
key={index} |
|||
className={`relative rounded-md sm:rounded-xl overflow-hidden ${ |
|||
index >= 2 ? "block" : "" |
|||
}`}
|
|||
> |
|||
<div className="aspect-w-4 aspect-h-3"> |
|||
<Image |
|||
fill |
|||
className="object-cover w-full h-full rounded-md sm:rounded-xl " |
|||
src={item || ""} |
|||
alt="photos" |
|||
sizes="400px" |
|||
/> |
|||
</div> |
|||
|
|||
{/* OVERLAY */} |
|||
<div |
|||
className="absolute inset-0 bg-neutral-900 bg-opacity-20 opacity-0 hover:opacity-100 transition-opacity cursor-pointer" |
|||
onClick={handleOpenModalImageGallery} |
|||
/> |
|||
</div> |
|||
))} |
|||
|
|||
<div |
|||
className="absolute hidden md:flex md:items-center md:justify-center left-3 bottom-3 px-4 py-2 rounded-xl bg-neutral-100 text-neutral-500 cursor-pointer hover:bg-neutral-200 z-10" |
|||
onClick={handleOpenModalImageGallery} |
|||
> |
|||
<Squares2X2Icon className="h-5 w-5" /> |
|||
|
|||
<span className="ml-2 text-neutral-800 text-sm font-medium"> |
|||
Show all photos |
|||
</span> |
|||
</div> |
|||
</div> |
|||
</header> |
|||
|
|||
{/* MAIn */} |
|||
<main className=" relative z-10 mt-11 flex flex-col lg:flex-row "> |
|||
{/* CONTENT */} |
|||
<div className="w-full lg:w-3/5 xl:w-2/3 space-y-8 lg:pr-10 lg:space-y-10"> |
|||
{renderSection1()} |
|||
<div className="block lg:hidden">{renderSidebarDetail()}</div> |
|||
{renderSectionTienIch()} |
|||
{renderSection2()} |
|||
{renderSection3()} |
|||
<SectionDateRange /> |
|||
|
|||
{renderSection5()} |
|||
{renderSection6()} |
|||
{renderSection7()} |
|||
{renderSection8()} |
|||
</div> |
|||
|
|||
{/* SIDEBAR */} |
|||
<div className="block flex-grow mt-14 lg:mt-0"> |
|||
{renderSidebarDetail()} |
|||
<div className="hidden lg:block mt-10 sticky top-28"> |
|||
{renderSidebarPrice()} |
|||
</div> |
|||
</div> |
|||
</main> |
|||
</div> |
|||
); |
|||
}; |
|||
|
|||
export default ListingCarDetailPage; |
@ -1,122 +0,0 @@ |
|||
"use client"; |
|||
|
|||
import React, { Fragment, FC, useState } from "react"; |
|||
import { Popover, Transition } from "@headlessui/react"; |
|||
import NcInputNumber from "@/components/NcInputNumber"; |
|||
import { UserPlusIcon } from "@heroicons/react/24/outline"; |
|||
import ClearDataButton from "@/app/(client-components)/(HeroSearchForm)/ClearDataButton"; |
|||
import { GuestsObject } from "@/app/(client-components)/type"; |
|||
|
|||
export interface GuestsInputProps { |
|||
className?: string; |
|||
} |
|||
|
|||
const GuestsInput: FC<GuestsInputProps> = ({ className = "flex-1" }) => { |
|||
const [guestAdultsInputValue, setGuestAdultsInputValue] = useState(2); |
|||
const [guestChildrenInputValue, setGuestChildrenInputValue] = useState(1); |
|||
const [guestInfantsInputValue, setGuestInfantsInputValue] = useState(1); |
|||
|
|||
const handleChangeData = (value: number, type: keyof GuestsObject) => { |
|||
let newValue = { |
|||
guestAdults: guestAdultsInputValue, |
|||
guestChildren: guestChildrenInputValue, |
|||
guestInfants: guestInfantsInputValue, |
|||
}; |
|||
if (type === "guestAdults") { |
|||
setGuestAdultsInputValue(value); |
|||
newValue.guestAdults = value; |
|||
} |
|||
if (type === "guestChildren") { |
|||
setGuestChildrenInputValue(value); |
|||
newValue.guestChildren = value; |
|||
} |
|||
if (type === "guestInfants") { |
|||
setGuestInfantsInputValue(value); |
|||
newValue.guestInfants = value; |
|||
} |
|||
}; |
|||
|
|||
const totalGuests = |
|||
guestChildrenInputValue + guestAdultsInputValue + guestInfantsInputValue; |
|||
|
|||
return ( |
|||
<Popover className={`flex relative ${className}`}> |
|||
{({ open }) => ( |
|||
<> |
|||
<div |
|||
className={`flex-1 flex items-center focus:outline-none rounded-b-3xl ${ |
|||
open ? "shadow-lg" : "" |
|||
}`}
|
|||
> |
|||
<Popover.Button |
|||
className={`relative z-10 flex-1 flex text-left items-center p-3 space-x-3 focus:outline-none`} |
|||
> |
|||
<div className="text-neutral-300 dark:text-neutral-400"> |
|||
<UserPlusIcon className="w-5 h-5 lg:w-7 lg:h-7" /> |
|||
</div> |
|||
<div className="flex-grow"> |
|||
<span className="block xl:text-lg font-semibold"> |
|||
{totalGuests || ""} Guests |
|||
</span> |
|||
<span className="block mt-1 text-sm text-neutral-400 leading-none font-light"> |
|||
{totalGuests ? "Guests" : "Add guests"} |
|||
</span> |
|||
</div> |
|||
|
|||
{!!totalGuests && open && ( |
|||
<ClearDataButton |
|||
onClick={() => { |
|||
setGuestAdultsInputValue(0); |
|||
setGuestChildrenInputValue(0); |
|||
setGuestInfantsInputValue(0); |
|||
}} |
|||
/> |
|||
)} |
|||
</Popover.Button> |
|||
</div> |
|||
|
|||
<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 right-0 z-10 w-full sm:min-w-[340px] max-w-sm bg-white dark:bg-neutral-800 top-full mt-3 py-5 sm:py-6 px-4 sm:px-8 rounded-3xl shadow-xl ring-1 ring-black ring-opacity-5 "> |
|||
<NcInputNumber |
|||
className="w-full" |
|||
defaultValue={guestAdultsInputValue} |
|||
onChange={(value) => handleChangeData(value, "guestAdults")} |
|||
max={10} |
|||
min={1} |
|||
label="Adults" |
|||
desc="Ages 13 or above" |
|||
/> |
|||
<NcInputNumber |
|||
className="w-full mt-6" |
|||
defaultValue={guestChildrenInputValue} |
|||
onChange={(value) => handleChangeData(value, "guestChildren")} |
|||
max={4} |
|||
label="Children" |
|||
desc="Ages 2–12" |
|||
/> |
|||
|
|||
<NcInputNumber |
|||
className="w-full mt-6" |
|||
defaultValue={guestInfantsInputValue} |
|||
onChange={(value) => handleChangeData(value, "guestInfants")} |
|||
max={4} |
|||
label="Infants" |
|||
desc="Ages 0–2" |
|||
/> |
|||
</Popover.Panel> |
|||
</Transition> |
|||
</> |
|||
)} |
|||
</Popover> |
|||
); |
|||
}; |
|||
|
|||
export default GuestsInput; |
@ -1,109 +0,0 @@ |
|||
"use client"; |
|||
|
|||
import React, { Fragment, useState, FC } from "react"; |
|||
import { Popover, Transition } from "@headlessui/react"; |
|||
import { CalendarIcon } from "@heroicons/react/24/outline"; |
|||
import DatePickerCustomHeaderTwoMonth from "@/components/DatePickerCustomHeaderTwoMonth"; |
|||
import DatePickerCustomDay from "@/components/DatePickerCustomDay"; |
|||
import DatePicker from "react-datepicker"; |
|||
import ClearDataButton from "@/app/(client-components)/(HeroSearchForm)/ClearDataButton"; |
|||
|
|||
export interface StayDatesRangeInputProps { |
|||
className?: string; |
|||
} |
|||
|
|||
const StayDatesRangeInput: FC<StayDatesRangeInputProps> = ({ |
|||
className = "flex-1", |
|||
}) => { |
|||
const [startDate, setStartDate] = useState<Date | null>( |
|||
new Date("2023/02/06") |
|||
); |
|||
const [endDate, setEndDate] = useState<Date | null>(new Date("2023/02/23")); |
|||
//
|
|||
|
|||
const onChangeDate = (dates: [Date | null, Date | null]) => { |
|||
const [start, end] = dates; |
|||
setStartDate(start); |
|||
setEndDate(end); |
|||
}; |
|||
|
|||
const renderInput = () => { |
|||
return ( |
|||
<> |
|||
<div className="text-neutral-300 dark:text-neutral-400"> |
|||
<CalendarIcon className="w-5 h-5 lg:w-7 lg:h-7" /> |
|||
</div> |
|||
<div className="flex-grow text-left"> |
|||
<span className="block xl:text-lg font-semibold"> |
|||
{startDate?.toLocaleDateString("en-US", { |
|||
month: "short", |
|||
day: "2-digit", |
|||
}) || "Add dates"} |
|||
{endDate |
|||
? " - " + |
|||
endDate?.toLocaleDateString("en-US", { |
|||
month: "short", |
|||
day: "2-digit", |
|||
}) |
|||
: ""} |
|||
</span> |
|||
<span className="block mt-1 text-sm text-neutral-400 leading-none font-light"> |
|||
{"Check in - Check out"} |
|||
</span> |
|||
</div> |
|||
</> |
|||
); |
|||
}; |
|||
|
|||
return ( |
|||
<Popover className={`StayDatesRangeInput z-10 relative flex ${className}`}> |
|||
{({ open }) => ( |
|||
<> |
|||
<Popover.Button |
|||
className={`flex-1 flex relative p-3 items-center space-x-3 focus:outline-none ${ |
|||
open ? "shadow-lg" : "" |
|||
}`}
|
|||
> |
|||
{renderInput()} |
|||
{startDate && open && ( |
|||
<ClearDataButton onClick={() => onChangeDate([null, null])} /> |
|||
)} |
|||
</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 left-auto xl:-right-10 right-0 z-10 mt-3 top-full w-screen max-w-sm px-4 sm:px-0 lg:max-w-3xl"> |
|||
<div className="overflow-hidden rounded-3xl shadow-lg ring-1 ring-black ring-opacity-5 bg-white dark:bg-neutral-800 p-8"> |
|||
<DatePicker |
|||
selected={startDate} |
|||
onChange={onChangeDate} |
|||
startDate={startDate} |
|||
endDate={endDate} |
|||
selectsRange |
|||
monthsShown={2} |
|||
showPopperArrow={false} |
|||
inline |
|||
renderCustomHeader={(p) => ( |
|||
<DatePickerCustomHeaderTwoMonth {...p} /> |
|||
)} |
|||
renderDayContents={(day, date) => ( |
|||
<DatePickerCustomDay dayOfMonth={day} date={date} /> |
|||
)} |
|||
/> |
|||
</div> |
|||
</Popover.Panel> |
|||
</Transition> |
|||
</> |
|||
)} |
|||
</Popover> |
|||
); |
|||
}; |
|||
|
|||
export default StayDatesRangeInput; |
@ -1,39 +0,0 @@ |
|||
import { ListingGalleryImage } from "@/components/listing-image-gallery/utils/types"; |
|||
|
|||
export const PHOTOS: string[] = [ |
|||
"https://images.pexels.com/photos/3225531/pexels-photo-3225531.jpeg?auto=compress&cs=tinysrgb&dpr=3&h=750&w=1260", |
|||
"https://images.pexels.com/photos/1154638/pexels-photo-1154638.jpeg?auto=compress&cs=tinysrgb&dpr=3&h=750&w=1260", |
|||
"https://images.pexels.com/photos/3851949/pexels-photo-3851949.jpeg?auto=compress&cs=tinysrgb&dpr=2&h=650&w=940", |
|||
"https://images.pexels.com/photos/3019019/pexels-photo-3019019.jpeg?auto=compress&cs=tinysrgb&dpr=3&h=750&w=1260", |
|||
"https://images.pexels.com/photos/6438752/pexels-photo-6438752.jpeg?auto=compress&cs=tinysrgb&dpr=2&h=750&w=1260", |
|||
"https://images.pexels.com/photos/1320686/pexels-photo-1320686.jpeg?auto=compress&cs=tinysrgb&dpr=2&h=750&w=1260", |
|||
"https://images.pexels.com/photos/261394/pexels-photo-261394.jpeg?auto=compress&cs=tinysrgb&dpr=2&h=750&w=1260", |
|||
"https://images.pexels.com/photos/2861361/pexels-photo-2861361.jpeg?auto=compress&cs=tinysrgb&dpr=2&h=750&w=1260", |
|||
"https://images.pexels.com/photos/2677398/pexels-photo-2677398.jpeg?auto=compress&cs=tinysrgb&dpr=2&h=750&w=1260", |
|||
"https://images.pexels.com/photos/4348078/pexels-photo-4348078.jpeg?auto=compress&cs=tinysrgb&w=1600", |
|||
"https://images.pexels.com/photos/3825527/pexels-photo-3825527.jpeg?auto=compress&cs=tinysrgb&w=1600", |
|||
"https://images.pexels.com/photos/4706134/pexels-photo-4706134.jpeg?auto=compress&cs=tinysrgb&w=1600", |
|||
"https://images.pexels.com/photos/3825578/pexels-photo-3825578.jpeg?auto=compress&cs=tinysrgb&w=1600", |
|||
"https://images.pexels.com/photos/123335/pexels-photo-123335.jpeg?auto=compress&cs=tinysrgb&w=1600", |
|||
"https://images.pexels.com/photos/3761124/pexels-photo-3761124.jpeg?auto=compress&cs=tinysrgb&w=1600", |
|||
"https://images.pexels.com/photos/8926846/pexels-photo-8926846.jpeg?auto=compress&cs=tinysrgb&w=1600", |
|||
"https://images.pexels.com/photos/4706139/pexels-photo-4706139.jpeg?auto=compress&cs=tinysrgb&w=1600", |
|||
"https://images.pexels.com/photos/7003624/pexels-photo-7003624.jpeg?auto=compress&cs=tinysrgb&w=1600", |
|||
]; |
|||
|
|||
export const includes_demo = [ |
|||
{ name: "Set Menu Lunch on boat" }, |
|||
{ name: "Express Bus From Hanoi To Halong and Return" }, |
|||
{ name: "Mineral Water On Express Bus" }, |
|||
{ name: "Kayak or Bamboo Boat. Life Jacket." }, |
|||
{ name: "Halong Bay Entrance Ticket" }, |
|||
]; |
|||
|
|||
export const imageGallery: ListingGalleryImage[] = [...PHOTOS].map( |
|||
(item, index): ListingGalleryImage => { |
|||
return { |
|||
id: index, |
|||
url: item, |
|||
}; |
|||
} |
|||
); |
@ -1,505 +0,0 @@ |
|||
"use client"; |
|||
|
|||
import React, { FC, useState } from "react"; |
|||
import { ArrowRightIcon, Squares2X2Icon } from "@heroicons/react/24/outline"; |
|||
import CommentListing from "@/components/CommentListing"; |
|||
import FiveStartIconForRate from "@/components/FiveStartIconForRate"; |
|||
import Avatar from "@/shared/Avatar"; |
|||
import Badge from "@/shared/Badge"; |
|||
import ButtonCircle from "@/shared/ButtonCircle"; |
|||
import ButtonPrimary from "@/shared/ButtonPrimary"; |
|||
import ButtonSecondary from "@/shared/ButtonSecondary"; |
|||
import Input from "@/shared/Input"; |
|||
import { usePathname, useRouter } from "next/navigation"; |
|||
import LikeSaveBtns from "@/components/LikeSaveBtns"; |
|||
import StartRating from "@/components/StartRating"; |
|||
import { includes_demo, PHOTOS } from "./constant"; |
|||
import Image from "next/image"; |
|||
import StayDatesRangeInput from "./StayDatesRangeInput"; |
|||
import GuestsInput from "./GuestsInput"; |
|||
import SectionDateRange from "../SectionDateRange"; |
|||
import { Route } from "next"; |
|||
|
|||
export interface ListingExperiencesDetailPageProps {} |
|||
|
|||
const ListingExperiencesDetailPage: FC< |
|||
ListingExperiencesDetailPageProps |
|||
> = ({}) => { |
|||
const thisPathname = usePathname(); |
|||
const router = useRouter(); |
|||
|
|||
const handleOpenModalImageGallery = () => { |
|||
router.push(`${thisPathname}/?modal=PHOTO_TOUR_SCROLLABLE` as Route); |
|||
}; |
|||
|
|||
const renderSection1 = () => { |
|||
return ( |
|||
<div className="listingSection__wrap !space-y-6"> |
|||
{/* 1 */} |
|||
<div className="flex justify-between items-center"> |
|||
<Badge color="pink" name="Specific Tour" /> |
|||
<LikeSaveBtns /> |
|||
</div> |
|||
|
|||
{/* 2 */} |
|||
<h2 className="text-2xl sm:text-3xl lg:text-4xl font-semibold"> |
|||
Trang An Boat Tour & Mua Cave |
|||
</h2> |
|||
|
|||
{/* 3 */} |
|||
<div className="flex items-center space-x-4"> |
|||
<StartRating /> |
|||
<span>·</span> |
|||
<span> |
|||
<i className="las la-map-marker-alt"></i> |
|||
<span className="ml-1"> Tokyo, Jappan</span> |
|||
</span> |
|||
</div> |
|||
|
|||
{/* 4 */} |
|||
<div className="flex items-center"> |
|||
<Avatar hasChecked sizeClass="h-10 w-10" radius="rounded-full" /> |
|||
<span className="ml-2.5 text-neutral-500 dark:text-neutral-400"> |
|||
Hosted by{" "} |
|||
<span className="text-neutral-900 dark:text-neutral-200 font-medium"> |
|||
Kevin Francis |
|||
</span> |
|||
</span> |
|||
</div> |
|||
|
|||
{/* 5 */} |
|||
<div className="w-full border-b border-neutral-100 dark:border-neutral-700" /> |
|||
|
|||
{/* 6 */} |
|||
<div className="flex items-center justify-between xl:justify-start space-x-8 xl:space-x-12 text-sm text-neutral-700 dark:text-neutral-300"> |
|||
<div className="flex flex-col sm:flex-row items-center space-y-3 sm:space-y-0 text-center sm:text-left sm:space-x-3 "> |
|||
<i className="las la-clock text-2xl"></i> |
|||
<span className="">3.5 hours</span> |
|||
</div> |
|||
<div className="flex flex-col sm:flex-row items-center space-y-3 sm:space-y-0 text-center sm:text-left sm:space-x-3 "> |
|||
<i className="las la-user-friends text-2xl"></i> |
|||
<span className="">Up to 10 people</span> |
|||
</div> |
|||
<div className="flex flex-col sm:flex-row items-center space-y-3 sm:space-y-0 text-center sm:text-left sm:space-x-3 "> |
|||
<i className="las la-language text-2xl"></i> |
|||
<span className="">English, VietNames</span> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
); |
|||
}; |
|||
|
|||
const renderSection2 = () => { |
|||
return ( |
|||
<div className="listingSection__wrap"> |
|||
<h2 className="text-2xl font-semibold">Experiences descriptions</h2> |
|||
<div className="w-14 border-b border-neutral-200 dark:border-neutral-700"></div> |
|||
<div className="text-neutral-6000 dark:text-neutral-300"> |
|||
<p> |
|||
TRANG AN BOAT TOUR & MUA CAVE CLIMBING TOUR FROM HANOI |
|||
<br /> |
|||
<br /> |
|||
07:30 – 08:00 – Our guide will meet you at your hotel/stay and start |
|||
a 120km comfortable Limousine bus journey through the verdant |
|||
landscape. Stopover for a rest on the way. |
|||
<br /> |
|||
<br /> |
|||
BAI DINH PAGODA EXPLORER. |
|||
<br /> |
|||
<br /> |
|||
10:30 – Arrive Bai Dinh pagoda complex, get on electric cars to |
|||
visit massive architecture. |
|||
<br /> |
|||
<br /> |
|||
12:15 – Enjoy the buffet lunch at our restaurant, a great place to |
|||
savor the flavours of Vietnamese food. |
|||
<br /> |
|||
<br /> |
|||
TRANG AN TOUR ON BOAT. |
|||
<br /> |
|||
<br /> |
|||
13:30 – Visit Trang An Grottoes, get on a rowing boat traveling |
|||
along the river with scenic mountain and green fields landscape. |
|||
<br /> |
|||
<br /> |
|||
MUA CAVE HIKING. TAKE PICTURE |
|||
<br /> |
|||
<br /> |
|||
15:45 – Arrive at Mua Cave and start an amazing trek up to the top |
|||
of Ngoa Long mountain. |
|||
<br /> |
|||
<br /> |
|||
17:30 – 20:00 – Return to our Limousine bus and then come back to |
|||
Hanoi. Drop you off at your hotel/stay. Other things to note |
|||
<br /> |
|||
<br /> |
|||
It is one full day tour. Start from 07.30 AM and finish at 20.00. We |
|||
just put one hour and default departure time because we have many |
|||
other tours. IF you need any further details |
|||
</p> |
|||
</div> |
|||
</div> |
|||
); |
|||
}; |
|||
|
|||
const renderSection3 = () => { |
|||
return ( |
|||
<div className="listingSection__wrap"> |
|||
<div> |
|||
<h2 className="text-2xl font-semibold">Include </h2> |
|||
<span className="block mt-2 text-neutral-500 dark:text-neutral-400"> |
|||
Included in the price |
|||
</span> |
|||
</div> |
|||
<div className="w-14 border-b border-neutral-200 dark:border-neutral-700"></div> |
|||
{/* 6 */} |
|||
<div className="grid grid-cols-1 lg:grid-cols-2 gap-6 text-sm text-neutral-700 dark:text-neutral-300 "> |
|||
{includes_demo |
|||
.filter((_, i) => i < 12) |
|||
.map((item) => ( |
|||
<div key={item.name} className="flex items-center space-x-3"> |
|||
<i className="las la-check-circle text-2xl"></i> |
|||
<span>{item.name}</span> |
|||
</div> |
|||
))} |
|||
</div> |
|||
</div> |
|||
); |
|||
}; |
|||
|
|||
const renderSection5 = () => { |
|||
return ( |
|||
<div className="listingSection__wrap"> |
|||
{/* HEADING */} |
|||
<h2 className="text-2xl font-semibold">Host Information</h2> |
|||
<div className="w-14 border-b border-neutral-200 dark:border-neutral-700"></div> |
|||
|
|||
{/* host */} |
|||
<div className="flex items-center space-x-4"> |
|||
<Avatar |
|||
hasChecked |
|||
hasCheckedClass="w-4 h-4 -top-0.5 right-0.5" |
|||
sizeClass="h-14 w-14" |
|||
radius="rounded-full" |
|||
/> |
|||
<div> |
|||
<a className="block text-xl font-medium" href="##"> |
|||
Kevin Francis |
|||
</a> |
|||
<div className="mt-1.5 flex items-center text-sm text-neutral-500 dark:text-neutral-400"> |
|||
<StartRating /> |
|||
<span className="mx-2">·</span> |
|||
<span> 12 places</span> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
|
|||
{/* desc */} |
|||
<span className="block text-neutral-6000 dark:text-neutral-300"> |
|||
Providing lake views, The Symphony 9 Tam Coc in Ninh Binh provides |
|||
accommodation, an outdoor swimming pool, a bar, a shared lounge, a |
|||
garden and barbecue facilities... |
|||
</span> |
|||
|
|||
{/* info */} |
|||
<div className="block text-neutral-500 dark:text-neutral-400 space-y-2.5"> |
|||
<div className="flex items-center space-x-3"> |
|||
<svg |
|||
xmlns="http://www.w3.org/2000/svg" |
|||
className="h-6 w-6" |
|||
fill="none" |
|||
viewBox="0 0 24 24" |
|||
stroke="currentColor" |
|||
> |
|||
<path |
|||
strokeLinecap="round" |
|||
strokeLinejoin="round" |
|||
strokeWidth={1.5} |
|||
d="M8 7V3m8 4V3m-9 8h10M5 21h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v12a2 2 0 002 2z" |
|||
/> |
|||
</svg> |
|||
<span>Joined in March 2016</span> |
|||
</div> |
|||
<div className="flex items-center space-x-3"> |
|||
<svg |
|||
xmlns="http://www.w3.org/2000/svg" |
|||
className="h-6 w-6" |
|||
fill="none" |
|||
viewBox="0 0 24 24" |
|||
stroke="currentColor" |
|||
> |
|||
<path |
|||
strokeLinecap="round" |
|||
strokeLinejoin="round" |
|||
strokeWidth={1.5} |
|||
d="M7 8h10M7 12h4m1 8l-4-4H5a2 2 0 01-2-2V6a2 2 0 012-2h14a2 2 0 012 2v8a2 2 0 01-2 2h-3l-4 4z" |
|||
/> |
|||
</svg> |
|||
<span>Response rate - 100%</span> |
|||
</div> |
|||
<div className="flex items-center space-x-3"> |
|||
<svg |
|||
xmlns="http://www.w3.org/2000/svg" |
|||
className="h-6 w-6" |
|||
fill="none" |
|||
viewBox="0 0 24 24" |
|||
stroke="currentColor" |
|||
> |
|||
<path |
|||
strokeLinecap="round" |
|||
strokeLinejoin="round" |
|||
strokeWidth={1.5} |
|||
d="M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z" |
|||
/> |
|||
</svg> |
|||
|
|||
<span>Fast response - within a few hours</span> |
|||
</div> |
|||
</div> |
|||
|
|||
{/* == */} |
|||
<div className="w-14 border-b border-neutral-200 dark:border-neutral-700"></div> |
|||
<div> |
|||
<ButtonSecondary href="/author">See host profile</ButtonSecondary> |
|||
</div> |
|||
</div> |
|||
); |
|||
}; |
|||
|
|||
const renderSection6 = () => { |
|||
return ( |
|||
<div className="listingSection__wrap"> |
|||
{/* HEADING */} |
|||
<h2 className="text-2xl font-semibold">Reviews (23 reviews)</h2> |
|||
<div className="w-14 border-b border-neutral-200 dark:border-neutral-700"></div> |
|||
|
|||
{/* Content */} |
|||
<div className="space-y-5"> |
|||
<FiveStartIconForRate iconClass="w-6 h-6" className="space-x-0.5" /> |
|||
<div className="relative"> |
|||
<Input |
|||
fontClass="" |
|||
sizeClass="h-16 px-4 py-3" |
|||
rounded="rounded-3xl" |
|||
placeholder="Share your thoughts ..." |
|||
/> |
|||
<ButtonCircle |
|||
className="absolute right-2 top-1/2 transform -translate-y-1/2" |
|||
size=" w-12 h-12 " |
|||
> |
|||
<ArrowRightIcon className="w-5 h-5" /> |
|||
</ButtonCircle> |
|||
</div> |
|||
</div> |
|||
|
|||
{/* comment */} |
|||
<div className="divide-y divide-neutral-100 dark:divide-neutral-800"> |
|||
<CommentListing className="py-8" /> |
|||
<CommentListing className="py-8" /> |
|||
<CommentListing className="py-8" /> |
|||
<CommentListing className="py-8" /> |
|||
<div className="pt-8"> |
|||
<ButtonSecondary>View more 20 reviews</ButtonSecondary> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
); |
|||
}; |
|||
|
|||
const renderSection7 = () => { |
|||
return ( |
|||
<div className="listingSection__wrap"> |
|||
{/* HEADING */} |
|||
<div> |
|||
<h2 className="text-2xl font-semibold">Location</h2> |
|||
<span className="block mt-2 text-neutral-500 dark:text-neutral-400"> |
|||
San Diego, CA, United States of America (SAN-San Diego Intl.) |
|||
</span> |
|||
</div> |
|||
<div className="w-14 border-b border-neutral-200 dark:border-neutral-700" /> |
|||
|
|||
{/* MAP */} |
|||
<div className="aspect-w-5 aspect-h-5 sm:aspect-h-3 ring-1 ring-black/10 rounded-xl z-0"> |
|||
<div className="rounded-xl overflow-hidden z-0"> |
|||
<iframe |
|||
width="100%" |
|||
height="100%" |
|||
loading="lazy" |
|||
allowFullScreen |
|||
referrerPolicy="no-referrer-when-downgrade" |
|||
src="https://www.google.com/maps/embed/v1/place?key=AIzaSyAGVJfZMAKYfZ71nzL_v5i3LjTTWnCYwTY&q=Eiffel+Tower,Paris+France" |
|||
></iframe> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
); |
|||
}; |
|||
|
|||
const renderSection8 = () => { |
|||
return ( |
|||
<div className="listingSection__wrap"> |
|||
{/* HEADING */} |
|||
<h2 className="text-2xl font-semibold">Things to know</h2> |
|||
<div className="w-14 border-b border-neutral-200 dark:border-neutral-700" /> |
|||
|
|||
{/* CONTENT */} |
|||
<div> |
|||
<h4 className="text-lg font-semibold">Cancellation policy</h4> |
|||
<span className="block mt-3 text-neutral-500 dark:text-neutral-400"> |
|||
Any experience can be canceled and fully refunded within 24 hours of |
|||
purchase, or at least 7 days before the experience starts. |
|||
</span> |
|||
</div> |
|||
<div className="w-14 border-b border-neutral-200 dark:border-neutral-700" /> |
|||
|
|||
{/* CONTENT */} |
|||
<div> |
|||
<h4 className="text-lg font-semibold">Guest requirements</h4> |
|||
<span className="block mt-3 text-neutral-500 dark:text-neutral-400"> |
|||
Up to 10 guests ages 4 and up can attend. Parents may also bring |
|||
children under 2 years of age. |
|||
</span> |
|||
</div> |
|||
<div className="w-14 border-b border-neutral-200 dark:border-neutral-700" /> |
|||
|
|||
{/* CONTENT */} |
|||
<div> |
|||
<h4 className="text-lg font-semibold">What to bring</h4> |
|||
<div className="prose sm:prose"> |
|||
<ul className="mt-3 text-neutral-500 dark:text-neutral-400 space-y-2"> |
|||
<li> |
|||
Formal Wear To Visit Bai Dinh Pagoda Be ready before 7.30 Am. |
|||
</li> |
|||
<li>We will pick up from 07.30 to 08.00 AM</li> |
|||
</ul> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
); |
|||
}; |
|||
|
|||
const renderSidebar = () => { |
|||
return ( |
|||
<div className="listingSectionSidebar__wrap shadow-xl"> |
|||
{/* PRICE */} |
|||
<div className="flex justify-between"> |
|||
<span className="text-3xl font-semibold"> |
|||
$19 |
|||
<span className="ml-1 text-base font-normal text-neutral-500 dark:text-neutral-400"> |
|||
/person |
|||
</span> |
|||
</span> |
|||
<StartRating /> |
|||
</div> |
|||
|
|||
{/* FORM */} |
|||
{/* FORM */} |
|||
<form className="flex flex-col border border-neutral-200 dark:border-neutral-700 rounded-3xl "> |
|||
<StayDatesRangeInput className="flex-1 z-[11]" /> |
|||
<div className="w-full border-b border-neutral-200 dark:border-neutral-700"></div> |
|||
<GuestsInput className="flex-1" /> |
|||
</form> |
|||
|
|||
{/* SUM */} |
|||
<div className="flex flex-col space-y-4"> |
|||
<div className="flex justify-between text-neutral-6000 dark:text-neutral-300"> |
|||
<span>$19 x 3 adults</span> |
|||
<span>$57</span> |
|||
</div> |
|||
<div className="flex justify-between text-neutral-6000 dark:text-neutral-300"> |
|||
<span>Service charge</span> |
|||
<span>$0</span> |
|||
</div> |
|||
<div className="border-b border-neutral-200 dark:border-neutral-700"></div> |
|||
<div className="flex justify-between font-semibold"> |
|||
<span>Total</span> |
|||
<span>$199</span> |
|||
</div> |
|||
</div> |
|||
|
|||
{/* SUBMIT */} |
|||
<ButtonPrimary href={"/checkout"}>Reserve</ButtonPrimary> |
|||
</div> |
|||
); |
|||
}; |
|||
|
|||
return ( |
|||
<div className={` nc-ListingExperiencesDetailPage `}> |
|||
{/* SINGLE HEADER */} |
|||
<header className="rounded-md sm:rounded-xl"> |
|||
<div className="relative grid grid-cols-4 gap-1 sm:gap-2"> |
|||
<div |
|||
className="col-span-3 row-span-3 relative rounded-md sm:rounded-xl overflow-hidden cursor-pointer" |
|||
onClick={handleOpenModalImageGallery} |
|||
> |
|||
<Image |
|||
alt="photo 1" |
|||
fill |
|||
className="object-cover rounded-md sm:rounded-xl" |
|||
src={PHOTOS[0]} |
|||
sizes="(max-width: 768px) 100vw, (max-width: 1200px) 50vw, 50vw" |
|||
/> |
|||
<div className="absolute inset-0 bg-neutral-900 bg-opacity-20 opacity-0 hover:opacity-100 transition-opacity"></div> |
|||
</div> |
|||
{PHOTOS.filter((_, i) => i >= 1 && i < 4).map((item, index) => ( |
|||
<div |
|||
key={index} |
|||
className={`relative rounded-md sm:rounded-xl overflow-hidden ${ |
|||
index >= 2 ? "block" : "" |
|||
}`}
|
|||
> |
|||
<div className="aspect-w-4 aspect-h-3"> |
|||
<Image |
|||
alt="photos" |
|||
fill |
|||
className="object-cover w-full h-full rounded-md sm:rounded-xl " |
|||
src={item || ""} |
|||
sizes="400px" |
|||
/> |
|||
</div> |
|||
|
|||
{/* OVERLAY */} |
|||
<div |
|||
className="absolute inset-0 bg-neutral-900 bg-opacity-20 opacity-0 hover:opacity-100 transition-opacity cursor-pointer" |
|||
onClick={handleOpenModalImageGallery} |
|||
/> |
|||
</div> |
|||
))} |
|||
|
|||
<div |
|||
className="absolute hidden md:flex md:items-center md:justify-center left-3 bottom-3 px-4 py-2 rounded-xl bg-neutral-100 text-neutral-500 cursor-pointer hover:bg-neutral-200 z-10" |
|||
onClick={handleOpenModalImageGallery} |
|||
> |
|||
<Squares2X2Icon className="h-5 w-5" /> |
|||
<span className="ml-2 text-neutral-800 text-sm font-medium"> |
|||
Show all photos |
|||
</span> |
|||
</div> |
|||
</div> |
|||
</header> |
|||
|
|||
{/* MAIn */} |
|||
<main className="relative z-10 mt-11 flex flex-col lg:flex-row "> |
|||
{/* CONTENT */} |
|||
<div className="w-full lg:w-3/5 xl:w-2/3 space-y-8 lg:pr-10 lg:space-y-10"> |
|||
{renderSection1()} |
|||
{renderSection2()} |
|||
{renderSection3()} |
|||
<SectionDateRange /> |
|||
|
|||
{renderSection5()} |
|||
{renderSection6()} |
|||
{renderSection7()} |
|||
{renderSection8()} |
|||
</div> |
|||
|
|||
{/* SIDEBAR */} |
|||
<div className="hidden lg:block flex-grow mt-14 lg:mt-0"> |
|||
<div className="sticky top-28">{renderSidebar()}</div> |
|||
</div> |
|||
</main> |
|||
</div> |
|||
); |
|||
}; |
|||
|
|||
export default ListingExperiencesDetailPage; |
@ -1,122 +0,0 @@ |
|||
"use client"; |
|||
|
|||
import React, { Fragment, FC, useState } from "react"; |
|||
import { Popover, Transition } from "@headlessui/react"; |
|||
import NcInputNumber from "@/components/NcInputNumber"; |
|||
import { UserPlusIcon } from "@heroicons/react/24/outline"; |
|||
import ClearDataButton from "@/app/(client-components)/(HeroSearchForm)/ClearDataButton"; |
|||
import { GuestsObject } from "@/app/(client-components)/type"; |
|||
|
|||
export interface GuestsInputProps { |
|||
className?: string; |
|||
} |
|||
|
|||
const GuestsInput: FC<GuestsInputProps> = ({ className = "flex-1" }) => { |
|||
const [guestAdultsInputValue, setGuestAdultsInputValue] = useState(2); |
|||
const [guestChildrenInputValue, setGuestChildrenInputValue] = useState(1); |
|||
const [guestInfantsInputValue, setGuestInfantsInputValue] = useState(1); |
|||
|
|||
const handleChangeData = (value: number, type: keyof GuestsObject) => { |
|||
let newValue = { |
|||
guestAdults: guestAdultsInputValue, |
|||
guestChildren: guestChildrenInputValue, |
|||
guestInfants: guestInfantsInputValue, |
|||
}; |
|||
if (type === "guestAdults") { |
|||
setGuestAdultsInputValue(value); |
|||
newValue.guestAdults = value; |
|||
} |
|||
if (type === "guestChildren") { |
|||
setGuestChildrenInputValue(value); |
|||
newValue.guestChildren = value; |
|||
} |
|||
if (type === "guestInfants") { |
|||
setGuestInfantsInputValue(value); |
|||
newValue.guestInfants = value; |
|||
} |
|||
}; |
|||
|
|||
const totalGuests = |
|||
guestChildrenInputValue + guestAdultsInputValue + guestInfantsInputValue; |
|||
|
|||
return ( |
|||
<Popover className={`flex relative ${className}`}> |
|||
{({ open }) => ( |
|||
<> |
|||
<div |
|||
className={`flex-1 flex items-center focus:outline-none rounded-b-3xl ${ |
|||
open ? "shadow-lg" : "" |
|||
}`}
|
|||
> |
|||
<Popover.Button |
|||
className={`relative z-10 flex-1 flex text-left items-center p-3 space-x-3 focus:outline-none`} |
|||
> |
|||
<div className="text-neutral-300 dark:text-neutral-400"> |
|||
<UserPlusIcon className="w-5 h-5 lg:w-7 lg:h-7" /> |
|||
</div> |
|||
<div className="flex-grow"> |
|||
<span className="block xl:text-lg font-semibold"> |
|||
{totalGuests || ""} Guests |
|||
</span> |
|||
<span className="block mt-1 text-sm text-neutral-400 leading-none font-light"> |
|||
{totalGuests ? "Guests" : "Add guests"} |
|||
</span> |
|||
</div> |
|||
|
|||
{!!totalGuests && open && ( |
|||
<ClearDataButton |
|||
onClick={() => { |
|||
setGuestAdultsInputValue(0); |
|||
setGuestChildrenInputValue(0); |
|||
setGuestInfantsInputValue(0); |
|||
}} |
|||
/> |
|||
)} |
|||
</Popover.Button> |
|||
</div> |
|||
|
|||
<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 right-0 z-10 w-full sm:min-w-[340px] max-w-sm bg-white dark:bg-neutral-800 top-full mt-3 py-5 sm:py-6 px-4 sm:px-8 rounded-3xl shadow-xl ring-1 ring-black ring-opacity-5 "> |
|||
<NcInputNumber |
|||
className="w-full" |
|||
defaultValue={guestAdultsInputValue} |
|||
onChange={(value) => handleChangeData(value, "guestAdults")} |
|||
max={10} |
|||
min={1} |
|||
label="Adults" |
|||
desc="Ages 13 or above" |
|||
/> |
|||
<NcInputNumber |
|||
className="w-full mt-6" |
|||
defaultValue={guestChildrenInputValue} |
|||
onChange={(value) => handleChangeData(value, "guestChildren")} |
|||
max={4} |
|||
label="Children" |
|||
desc="Ages 2–12" |
|||
/> |
|||
|
|||
<NcInputNumber |
|||
className="w-full mt-6" |
|||
defaultValue={guestInfantsInputValue} |
|||
onChange={(value) => handleChangeData(value, "guestInfants")} |
|||
max={4} |
|||
label="Infants" |
|||
desc="Ages 0–2" |
|||
/> |
|||
</Popover.Panel> |
|||
</Transition> |
|||
</> |
|||
)} |
|||
</Popover> |
|||
); |
|||
}; |
|||
|
|||
export default GuestsInput; |
@ -1,109 +0,0 @@ |
|||
"use client"; |
|||
|
|||
import React, { Fragment, useState, FC } from "react"; |
|||
import { Popover, Transition } from "@headlessui/react"; |
|||
import { CalendarIcon } from "@heroicons/react/24/outline"; |
|||
import DatePickerCustomHeaderTwoMonth from "@/components/DatePickerCustomHeaderTwoMonth"; |
|||
import DatePickerCustomDay from "@/components/DatePickerCustomDay"; |
|||
import DatePicker from "react-datepicker"; |
|||
import ClearDataButton from "@/app/(client-components)/(HeroSearchForm)/ClearDataButton"; |
|||
|
|||
export interface StayDatesRangeInputProps { |
|||
className?: string; |
|||
} |
|||
|
|||
const StayDatesRangeInput: FC<StayDatesRangeInputProps> = ({ |
|||
className = "flex-1", |
|||
}) => { |
|||
const [startDate, setStartDate] = useState<Date | null>( |
|||
new Date("2023/02/06") |
|||
); |
|||
const [endDate, setEndDate] = useState<Date | null>(new Date("2023/02/23")); |
|||
//
|
|||
|
|||
const onChangeDate = (dates: [Date | null, Date | null]) => { |
|||
const [start, end] = dates; |
|||
setStartDate(start); |
|||
setEndDate(end); |
|||
}; |
|||
|
|||
const renderInput = () => { |
|||
return ( |
|||
<> |
|||
<div className="text-neutral-300 dark:text-neutral-400"> |
|||
<CalendarIcon className="w-5 h-5 lg:w-7 lg:h-7" /> |
|||
</div> |
|||
<div className="flex-grow text-left"> |
|||
<span className="block xl:text-lg font-semibold"> |
|||
{startDate?.toLocaleDateString("en-US", { |
|||
month: "short", |
|||
day: "2-digit", |
|||
}) || "Add dates"} |
|||
{endDate |
|||
? " - " + |
|||
endDate?.toLocaleDateString("en-US", { |
|||
month: "short", |
|||
day: "2-digit", |
|||
}) |
|||
: ""} |
|||
</span> |
|||
<span className="block mt-1 text-sm text-neutral-400 leading-none font-light"> |
|||
{"Check in - Check out"} |
|||
</span> |
|||
</div> |
|||
</> |
|||
); |
|||
}; |
|||
|
|||
return ( |
|||
<Popover className={`StayDatesRangeInput z-10 relative flex ${className}`}> |
|||
{({ open }) => ( |
|||
<> |
|||
<Popover.Button |
|||
className={`flex-1 flex relative p-3 items-center space-x-3 focus:outline-none ${ |
|||
open ? "shadow-lg" : "" |
|||
}`}
|
|||
> |
|||
{renderInput()} |
|||
{startDate && open && ( |
|||
<ClearDataButton onClick={() => onChangeDate([null, null])} /> |
|||
)} |
|||
</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 left-auto xl:-right-10 right-0 z-10 mt-3 top-full w-screen max-w-sm px-4 sm:px-0 lg:max-w-3xl"> |
|||
<div className="overflow-hidden rounded-3xl shadow-lg ring-1 ring-black ring-opacity-5 bg-white dark:bg-neutral-800 p-8"> |
|||
<DatePicker |
|||
selected={startDate} |
|||
onChange={onChangeDate} |
|||
startDate={startDate} |
|||
endDate={endDate} |
|||
selectsRange |
|||
monthsShown={2} |
|||
showPopperArrow={false} |
|||
inline |
|||
renderCustomHeader={(p) => ( |
|||
<DatePickerCustomHeaderTwoMonth {...p} /> |
|||
)} |
|||
renderDayContents={(day, date) => ( |
|||
<DatePickerCustomDay dayOfMonth={day} date={date} /> |
|||
)} |
|||
/> |
|||
</div> |
|||
</Popover.Panel> |
|||
</Transition> |
|||
</> |
|||
)} |
|||
</Popover> |
|||
); |
|||
}; |
|||
|
|||
export default StayDatesRangeInput; |
@ -1,71 +0,0 @@ |
|||
import { ListingGalleryImage } from "@/components/listing-image-gallery/utils/types"; |
|||
|
|||
export const PHOTOS: string[] = [ |
|||
"https://images.pexels.com/photos/6129967/pexels-photo-6129967.jpeg?auto=compress&cs=tinysrgb&dpr=3&h=750&w=1260", |
|||
"https://images.pexels.com/photos/7163619/pexels-photo-7163619.jpeg?auto=compress&cs=tinysrgb&dpr=2&h=750&w=1260", |
|||
"https://images.pexels.com/photos/6527036/pexels-photo-6527036.jpeg?auto=compress&cs=tinysrgb&dpr=2&h=750&w=1260", |
|||
"https://images.pexels.com/photos/6969831/pexels-photo-6969831.jpeg?auto=compress&cs=tinysrgb&dpr=2&h=750&w=1260", |
|||
"https://images.pexels.com/photos/6438752/pexels-photo-6438752.jpeg?auto=compress&cs=tinysrgb&dpr=2&h=750&w=1260", |
|||
"https://images.pexels.com/photos/1320686/pexels-photo-1320686.jpeg?auto=compress&cs=tinysrgb&dpr=2&h=750&w=1260", |
|||
"https://images.pexels.com/photos/261394/pexels-photo-261394.jpeg?auto=compress&cs=tinysrgb&dpr=2&h=750&w=1260", |
|||
"https://images.pexels.com/photos/2861361/pexels-photo-2861361.jpeg?auto=compress&cs=tinysrgb&dpr=2&h=750&w=1260", |
|||
"https://images.pexels.com/photos/2677398/pexels-photo-2677398.jpeg?auto=compress&cs=tinysrgb&dpr=2&h=750&w=1260", |
|||
"https://images.pexels.com/photos/1365425/pexels-photo-1365425.jpeg?auto=compress&cs=tinysrgb&w=1600", |
|||
"https://images.pexels.com/photos/914128/pexels-photo-914128.jpeg?auto=compress&cs=tinysrgb&w=1600", |
|||
"https://images.pexels.com/photos/840667/pexels-photo-840667.jpeg?auto=compress&cs=tinysrgb&w=1600", |
|||
"https://images.pexels.com/photos/732632/pexels-photo-732632.jpeg?auto=compress&cs=tinysrgb&w=1600", |
|||
"https://images.pexels.com/photos/450062/pexels-photo-450062.jpeg?auto=compress&cs=tinysrgb&w=1600", |
|||
"https://images.pexels.com/photos/917510/pexels-photo-917510.jpeg?auto=compress&cs=tinysrgb&w=1600", |
|||
"https://images.pexels.com/photos/1194233/pexels-photo-1194233.jpeg?auto=compress&cs=tinysrgb&w=1600", |
|||
"https://images.pexels.com/photos/236973/pexels-photo-236973.jpeg?auto=compress&cs=tinysrgb&w=1600", |
|||
"https://images.pexels.com/photos/1392099/pexels-photo-1392099.jpeg?auto=compress&cs=tinysrgb&w=1600", |
|||
"https://images.pexels.com/photos/547116/pexels-photo-547116.jpeg?auto=compress&cs=tinysrgb&w=1600", |
|||
"https://images.pexels.com/photos/1002272/pexels-photo-1002272.jpeg?auto=compress&cs=tinysrgb&w=1600", |
|||
"https://images.pexels.com/photos/917511/pexels-photo-917511.jpeg?auto=compress&cs=tinysrgb&w=1600", |
|||
"https://images.pexels.com/photos/771079/pexels-photo-771079.jpeg?auto=compress&cs=tinysrgb&w=1600", |
|||
"https://images.pexels.com/photos/13461077/pexels-photo-13461077.jpeg?auto=compress&cs=tinysrgb&w=1600&lazy=load", |
|||
"https://images.pexels.com/photos/9074921/pexels-photo-9074921.jpeg?auto=compress&cs=tinysrgb&w=1600&lazy=load", |
|||
"https://images.pexels.com/photos/9336042/pexels-photo-9336042.jpeg?auto=compress&cs=tinysrgb&w=1600&lazy=load", |
|||
"https://images.pexels.com/photos/5418318/pexels-photo-5418318.jpeg?auto=compress&cs=tinysrgb&w=1600&lazy=load", |
|||
"https://images.pexels.com/photos/4815278/pexels-photo-4815278.jpeg?auto=compress&cs=tinysrgb&w=1600&lazy=load", |
|||
"https://images.pexels.com/photos/1365425/pexels-photo-1365425.jpeg?auto=compress&cs=tinysrgb&w=1600", |
|||
]; |
|||
|
|||
export const Amenities_demos = [ |
|||
{ name: "la-key", icon: "la-key" }, |
|||
{ name: "la-luggage-cart", icon: "la-luggage-cart" }, |
|||
{ name: "la-shower", icon: "la-shower" }, |
|||
{ name: "la-smoking", icon: "la-smoking" }, |
|||
{ name: "la-snowflake", icon: "la-snowflake" }, |
|||
{ name: "la-spa", icon: "la-spa" }, |
|||
{ name: "la-suitcase", icon: "la-suitcase" }, |
|||
{ name: "la-suitcase-rolling", icon: "la-suitcase-rolling" }, |
|||
{ name: "la-swimmer", icon: "la-swimmer" }, |
|||
{ name: "la-swimming-pool", icon: "la-swimming-pool" }, |
|||
{ name: "la-tv", icon: "la-tv" }, |
|||
{ name: "la-umbrella-beach", icon: "la-umbrella-beach" }, |
|||
{ name: "la-utensils", icon: "la-utensils" }, |
|||
{ name: "la-wheelchair", icon: "la-wheelchair" }, |
|||
{ name: "la-wifi", icon: "la-wifi" }, |
|||
{ name: "la-baby-carriage", icon: "la-baby-carriage" }, |
|||
{ name: "la-bath", icon: "la-bath" }, |
|||
{ name: "la-bed", icon: "la-bed" }, |
|||
{ name: "la-briefcase", icon: "la-briefcase" }, |
|||
{ name: "la-car", icon: "la-car" }, |
|||
{ name: "la-cocktail", icon: "la-cocktail" }, |
|||
{ name: "la-coffee", icon: "la-coffee" }, |
|||
{ name: "la-concierge-bell", icon: "la-concierge-bell" }, |
|||
{ name: "la-dice", icon: "la-dice" }, |
|||
{ name: "la-dumbbell", icon: "la-dumbbell" }, |
|||
{ name: "la-hot-tub", icon: "la-hot-tub" }, |
|||
{ name: "la-infinity", icon: "la-infinity" }, |
|||
]; |
|||
|
|||
export const imageGallery: ListingGalleryImage[] = [...PHOTOS].map( |
|||
(item, index): ListingGalleryImage => { |
|||
return { |
|||
id: index, |
|||
url: item, |
|||
}; |
|||
} |
|||
); |
@ -1,637 +0,0 @@ |
|||
"use client"; |
|||
|
|||
import React, { FC, Fragment, useState } from "react"; |
|||
import { Dialog, Transition } from "@headlessui/react"; |
|||
import { ArrowRightIcon, Squares2X2Icon } from "@heroicons/react/24/outline"; |
|||
import CommentListing from "@/components/CommentListing"; |
|||
import FiveStartIconForRate from "@/components/FiveStartIconForRate"; |
|||
import StartRating from "@/components/StartRating"; |
|||
import Avatar from "@/shared/Avatar"; |
|||
import Badge from "@/shared/Badge"; |
|||
import ButtonCircle from "@/shared/ButtonCircle"; |
|||
import ButtonPrimary from "@/shared/ButtonPrimary"; |
|||
import ButtonSecondary from "@/shared/ButtonSecondary"; |
|||
import ButtonClose from "@/shared/ButtonClose"; |
|||
import Input from "@/shared/Input"; |
|||
import LikeSaveBtns from "@/components/LikeSaveBtns"; |
|||
import Image from "next/image"; |
|||
import { usePathname, useRouter } from "next/navigation"; |
|||
import { Amenities_demos, PHOTOS } from "./constant"; |
|||
import StayDatesRangeInput from "./StayDatesRangeInput"; |
|||
import GuestsInput from "./GuestsInput"; |
|||
import SectionDateRange from "../SectionDateRange"; |
|||
import { Route } from "next"; |
|||
|
|||
export interface ListingStayDetailPageProps {} |
|||
|
|||
const ListingStayDetailPage: FC<ListingStayDetailPageProps> = ({}) => { |
|||
//
|
|||
|
|||
let [isOpenModalAmenities, setIsOpenModalAmenities] = useState(false); |
|||
|
|||
const thisPathname = usePathname(); |
|||
const router = useRouter(); |
|||
|
|||
function closeModalAmenities() { |
|||
setIsOpenModalAmenities(false); |
|||
} |
|||
|
|||
function openModalAmenities() { |
|||
setIsOpenModalAmenities(true); |
|||
} |
|||
|
|||
const handleOpenModalImageGallery = () => { |
|||
router.push(`${thisPathname}/?modal=PHOTO_TOUR_SCROLLABLE` as Route); |
|||
}; |
|||
|
|||
const renderSection1 = () => { |
|||
return ( |
|||
<div className="listingSection__wrap !space-y-6"> |
|||
{/* 1 */} |
|||
<div className="flex justify-between items-center"> |
|||
<Badge name="Wooden house" /> |
|||
<LikeSaveBtns /> |
|||
</div> |
|||
|
|||
{/* 2 */} |
|||
<h2 className="text-2xl sm:text-3xl lg:text-4xl font-semibold"> |
|||
Beach House in Collingwood |
|||
</h2> |
|||
|
|||
{/* 3 */} |
|||
<div className="flex items-center space-x-4"> |
|||
<StartRating /> |
|||
<span>·</span> |
|||
<span> |
|||
<i className="las la-map-marker-alt"></i> |
|||
<span className="ml-1"> Tokyo, Jappan</span> |
|||
</span> |
|||
</div> |
|||
|
|||
{/* 4 */} |
|||
<div className="flex items-center"> |
|||
<Avatar hasChecked sizeClass="h-10 w-10" radius="rounded-full" /> |
|||
<span className="ml-2.5 text-neutral-500 dark:text-neutral-400"> |
|||
Hosted by{" "} |
|||
<span className="text-neutral-900 dark:text-neutral-200 font-medium"> |
|||
Kevin Francis |
|||
</span> |
|||
</span> |
|||
</div> |
|||
|
|||
{/* 5 */} |
|||
<div className="w-full border-b border-neutral-100 dark:border-neutral-700" /> |
|||
|
|||
{/* 6 */} |
|||
<div className="flex items-center justify-between xl:justify-start space-x-8 xl:space-x-12 text-sm text-neutral-700 dark:text-neutral-300"> |
|||
<div className="flex items-center space-x-3 "> |
|||
<i className=" las la-user text-2xl "></i> |
|||
<span className=""> |
|||
6 <span className="hidden sm:inline-block">guests</span> |
|||
</span> |
|||
</div> |
|||
<div className="flex items-center space-x-3"> |
|||
<i className=" las la-bed text-2xl"></i> |
|||
<span className=" "> |
|||
6 <span className="hidden sm:inline-block">beds</span> |
|||
</span> |
|||
</div> |
|||
<div className="flex items-center space-x-3"> |
|||
<i className=" las la-bath text-2xl"></i> |
|||
<span className=" "> |
|||
3 <span className="hidden sm:inline-block">baths</span> |
|||
</span> |
|||
</div> |
|||
<div className="flex items-center space-x-3"> |
|||
<i className=" las la-door-open text-2xl"></i> |
|||
<span className=" "> |
|||
2 <span className="hidden sm:inline-block">bedrooms</span> |
|||
</span> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
); |
|||
}; |
|||
|
|||
const renderSection2 = () => { |
|||
return ( |
|||
<div className="listingSection__wrap"> |
|||
<h2 className="text-2xl font-semibold">Stay information</h2> |
|||
<div className="w-14 border-b border-neutral-200 dark:border-neutral-700"></div> |
|||
<div className="text-neutral-6000 dark:text-neutral-300"> |
|||
<span> |
|||
Providing lake views, The Symphony 9 Tam Coc in Ninh Binh provides |
|||
accommodation, an outdoor swimming pool, a bar, a shared lounge, a |
|||
garden and barbecue facilities. Complimentary WiFi is provided. |
|||
</span> |
|||
<br /> |
|||
<br /> |
|||
<span> |
|||
There is a private bathroom with bidet in all units, along with a |
|||
hairdryer and free toiletries. |
|||
</span> |
|||
<br /> <br /> |
|||
<span> |
|||
The Symphony 9 Tam Coc offers a terrace. Both a bicycle rental |
|||
service and a car rental service are available at the accommodation, |
|||
while cycling can be enjoyed nearby. |
|||
</span> |
|||
</div> |
|||
</div> |
|||
); |
|||
}; |
|||
|
|||
const renderSection3 = () => { |
|||
return ( |
|||
<div className="listingSection__wrap"> |
|||
<div> |
|||
<h2 className="text-2xl font-semibold">Amenities </h2> |
|||
<span className="block mt-2 text-neutral-500 dark:text-neutral-400"> |
|||
{` About the property's amenities and services`} |
|||
</span> |
|||
</div> |
|||
<div className="w-14 border-b border-neutral-200 dark:border-neutral-700"></div> |
|||
{/* 6 */} |
|||
<div className="grid grid-cols-1 xl:grid-cols-3 gap-6 text-sm text-neutral-700 dark:text-neutral-300 "> |
|||
{Amenities_demos.filter((_, i) => i < 12).map((item) => ( |
|||
<div key={item.name} className="flex items-center space-x-3"> |
|||
<i className={`text-3xl las ${item.icon}`}></i> |
|||
<span className=" ">{item.name}</span> |
|||
</div> |
|||
))} |
|||
</div> |
|||
|
|||
{/* ----- */} |
|||
<div className="w-14 border-b border-neutral-200"></div> |
|||
<div> |
|||
<ButtonSecondary onClick={openModalAmenities}> |
|||
View more 20 amenities |
|||
</ButtonSecondary> |
|||
</div> |
|||
{renderMotalAmenities()} |
|||
</div> |
|||
); |
|||
}; |
|||
|
|||
const renderMotalAmenities = () => { |
|||
return ( |
|||
<Transition appear show={isOpenModalAmenities} as={Fragment}> |
|||
<Dialog |
|||
as="div" |
|||
className="fixed inset-0 z-50 overflow-y-auto" |
|||
onClose={closeModalAmenities} |
|||
> |
|||
<div className="min-h-screen px-4 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" /> |
|||
</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 |
|||
as={Fragment} |
|||
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-block py-8 h-screen w-full max-w-4xl"> |
|||
<div className="inline-flex pb-2 flex-col w-full 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"> |
|||
<h3 |
|||
className="text-lg font-medium leading-6 text-gray-900" |
|||
id="headlessui-dialog-title-70" |
|||
> |
|||
Amenities |
|||
</h3> |
|||
<span className="absolute left-3 top-3"> |
|||
<ButtonClose onClick={closeModalAmenities} /> |
|||
</span> |
|||
</div> |
|||
<div className="px-8 overflow-auto text-neutral-700 dark:text-neutral-300 divide-y divide-neutral-200"> |
|||
{Amenities_demos.filter((_, i) => i < 1212).map((item) => ( |
|||
<div |
|||
key={item.name} |
|||
className="flex items-center py-2.5 sm:py-4 lg:py-5 space-x-5 lg:space-x-8" |
|||
> |
|||
<i |
|||
className={`text-4xl text-neutral-6000 las ${item.icon}`} |
|||
></i> |
|||
<span>{item.name}</span> |
|||
</div> |
|||
))} |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</Transition.Child> |
|||
</div> |
|||
</Dialog> |
|||
</Transition> |
|||
); |
|||
}; |
|||
|
|||
const renderSection4 = () => { |
|||
return ( |
|||
<div className="listingSection__wrap"> |
|||
{/* HEADING */} |
|||
<div> |
|||
<h2 className="text-2xl font-semibold">Room Rates </h2> |
|||
<span className="block mt-2 text-neutral-500 dark:text-neutral-400"> |
|||
Prices may increase on weekends or holidays |
|||
</span> |
|||
</div> |
|||
<div className="w-14 border-b border-neutral-200 dark:border-neutral-700"></div> |
|||
{/* CONTENT */} |
|||
<div className="flow-root"> |
|||
<div className="text-sm sm:text-base text-neutral-6000 dark:text-neutral-300 -mb-4"> |
|||
<div className="p-4 bg-neutral-100 dark:bg-neutral-800 flex justify-between items-center space-x-4 rounded-lg"> |
|||
<span>Monday - Thursday</span> |
|||
<span>$199</span> |
|||
</div> |
|||
<div className="p-4 flex justify-between items-center space-x-4 rounded-lg"> |
|||
<span>Monday - Thursday</span> |
|||
<span>$199</span> |
|||
</div> |
|||
<div className="p-4 bg-neutral-100 dark:bg-neutral-800 flex justify-between items-center space-x-4 rounded-lg"> |
|||
<span>Friday - Sunday</span> |
|||
<span>$219</span> |
|||
</div> |
|||
<div className="p-4 flex justify-between items-center space-x-4 rounded-lg"> |
|||
<span>Rent by month</span> |
|||
<span>-8.34 %</span> |
|||
</div> |
|||
<div className="p-4 bg-neutral-100 dark:bg-neutral-800 flex justify-between items-center space-x-4 rounded-lg"> |
|||
<span>Minimum number of nights</span> |
|||
<span>1 night</span> |
|||
</div> |
|||
<div className="p-4 flex justify-between items-center space-x-4 rounded-lg"> |
|||
<span>Max number of nights</span> |
|||
<span>90 nights</span> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
); |
|||
}; |
|||
|
|||
const renderSection5 = () => { |
|||
return ( |
|||
<div className="listingSection__wrap"> |
|||
{/* HEADING */} |
|||
<h2 className="text-2xl font-semibold">Host Information</h2> |
|||
<div className="w-14 border-b border-neutral-200 dark:border-neutral-700"></div> |
|||
|
|||
{/* host */} |
|||
<div className="flex items-center space-x-4"> |
|||
<Avatar |
|||
hasChecked |
|||
hasCheckedClass="w-4 h-4 -top-0.5 right-0.5" |
|||
sizeClass="h-14 w-14" |
|||
radius="rounded-full" |
|||
/> |
|||
<div> |
|||
<a className="block text-xl font-medium" href="##"> |
|||
Kevin Francis |
|||
</a> |
|||
<div className="mt-1.5 flex items-center text-sm text-neutral-500 dark:text-neutral-400"> |
|||
<StartRating /> |
|||
<span className="mx-2">·</span> |
|||
<span> 12 places</span> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
|
|||
{/* desc */} |
|||
<span className="block text-neutral-6000 dark:text-neutral-300"> |
|||
Providing lake views, The Symphony 9 Tam Coc in Ninh Binh provides |
|||
accommodation, an outdoor swimming pool, a bar, a shared lounge, a |
|||
garden and barbecue facilities... |
|||
</span> |
|||
|
|||
{/* info */} |
|||
<div className="block text-neutral-500 dark:text-neutral-400 space-y-2.5"> |
|||
<div className="flex items-center space-x-3"> |
|||
<svg |
|||
xmlns="http://www.w3.org/2000/svg" |
|||
className="h-6 w-6" |
|||
fill="none" |
|||
viewBox="0 0 24 24" |
|||
stroke="currentColor" |
|||
> |
|||
<path |
|||
strokeLinecap="round" |
|||
strokeLinejoin="round" |
|||
strokeWidth={1.5} |
|||
d="M8 7V3m8 4V3m-9 8h10M5 21h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v12a2 2 0 002 2z" |
|||
/> |
|||
</svg> |
|||
<span>Joined in March 2016</span> |
|||
</div> |
|||
<div className="flex items-center space-x-3"> |
|||
<svg |
|||
xmlns="http://www.w3.org/2000/svg" |
|||
className="h-6 w-6" |
|||
fill="none" |
|||
viewBox="0 0 24 24" |
|||
stroke="currentColor" |
|||
> |
|||
<path |
|||
strokeLinecap="round" |
|||
strokeLinejoin="round" |
|||
strokeWidth={1.5} |
|||
d="M7 8h10M7 12h4m1 8l-4-4H5a2 2 0 01-2-2V6a2 2 0 012-2h14a2 2 0 012 2v8a2 2 0 01-2 2h-3l-4 4z" |
|||
/> |
|||
</svg> |
|||
<span>Response rate - 100%</span> |
|||
</div> |
|||
<div className="flex items-center space-x-3"> |
|||
<svg |
|||
xmlns="http://www.w3.org/2000/svg" |
|||
className="h-6 w-6" |
|||
fill="none" |
|||
viewBox="0 0 24 24" |
|||
stroke="currentColor" |
|||
> |
|||
<path |
|||
strokeLinecap="round" |
|||
strokeLinejoin="round" |
|||
strokeWidth={1.5} |
|||
d="M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z" |
|||
/> |
|||
</svg> |
|||
|
|||
<span>Fast response - within a few hours</span> |
|||
</div> |
|||
</div> |
|||
|
|||
{/* == */} |
|||
<div className="w-14 border-b border-neutral-200 dark:border-neutral-700"></div> |
|||
<div> |
|||
<ButtonSecondary href="/author">See host profile</ButtonSecondary> |
|||
</div> |
|||
</div> |
|||
); |
|||
}; |
|||
|
|||
const renderSection6 = () => { |
|||
return ( |
|||
<div className="listingSection__wrap"> |
|||
{/* HEADING */} |
|||
<h2 className="text-2xl font-semibold">Reviews (23 reviews)</h2> |
|||
<div className="w-14 border-b border-neutral-200 dark:border-neutral-700"></div> |
|||
|
|||
{/* Content */} |
|||
<div className="space-y-5"> |
|||
<FiveStartIconForRate iconClass="w-6 h-6" className="space-x-0.5" /> |
|||
<div className="relative"> |
|||
<Input |
|||
fontClass="" |
|||
sizeClass="h-16 px-4 py-3" |
|||
rounded="rounded-3xl" |
|||
placeholder="Share your thoughts ..." |
|||
/> |
|||
<ButtonCircle |
|||
className="absolute right-2 top-1/2 transform -translate-y-1/2" |
|||
size=" w-12 h-12 " |
|||
> |
|||
<ArrowRightIcon className="w-5 h-5" /> |
|||
</ButtonCircle> |
|||
</div> |
|||
</div> |
|||
|
|||
{/* comment */} |
|||
<div className="divide-y divide-neutral-100 dark:divide-neutral-800"> |
|||
<CommentListing className="py-8" /> |
|||
<CommentListing className="py-8" /> |
|||
<CommentListing className="py-8" /> |
|||
<CommentListing className="py-8" /> |
|||
<div className="pt-8"> |
|||
<ButtonSecondary>View more 20 reviews</ButtonSecondary> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
); |
|||
}; |
|||
|
|||
const renderSection7 = () => { |
|||
return ( |
|||
<div className="listingSection__wrap"> |
|||
{/* HEADING */} |
|||
<div> |
|||
<h2 className="text-2xl font-semibold">Location</h2> |
|||
<span className="block mt-2 text-neutral-500 dark:text-neutral-400"> |
|||
San Diego, CA, United States of America (SAN-San Diego Intl.) |
|||
</span> |
|||
</div> |
|||
<div className="w-14 border-b border-neutral-200 dark:border-neutral-700" /> |
|||
|
|||
{/* MAP */} |
|||
<div className="aspect-w-5 aspect-h-5 sm:aspect-h-3 ring-1 ring-black/10 rounded-xl z-0"> |
|||
<div className="rounded-xl overflow-hidden z-0"> |
|||
<iframe |
|||
width="100%" |
|||
height="100%" |
|||
loading="lazy" |
|||
allowFullScreen |
|||
referrerPolicy="no-referrer-when-downgrade" |
|||
src="https://www.google.com/maps/embed/v1/place?key=AIzaSyAGVJfZMAKYfZ71nzL_v5i3LjTTWnCYwTY&q=Eiffel+Tower,Paris+France" |
|||
></iframe> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
); |
|||
}; |
|||
|
|||
const renderSection8 = () => { |
|||
return ( |
|||
<div className="listingSection__wrap"> |
|||
{/* HEADING */} |
|||
<h2 className="text-2xl font-semibold">Things to know</h2> |
|||
<div className="w-14 border-b border-neutral-200 dark:border-neutral-700" /> |
|||
|
|||
{/* CONTENT */} |
|||
<div> |
|||
<h4 className="text-lg font-semibold">Cancellation policy</h4> |
|||
<span className="block mt-3 text-neutral-500 dark:text-neutral-400"> |
|||
Refund 50% of the booking value when customers cancel the room |
|||
within 48 hours after successful booking and 14 days before the |
|||
check-in time. <br /> |
|||
Then, cancel the room 14 days before the check-in time, get a 50% |
|||
refund of the total amount paid (minus the service fee). |
|||
</span> |
|||
</div> |
|||
<div className="w-14 border-b border-neutral-200 dark:border-neutral-700" /> |
|||
|
|||
{/* CONTENT */} |
|||
<div> |
|||
<h4 className="text-lg font-semibold">Check-in time</h4> |
|||
<div className="mt-3 text-neutral-500 dark:text-neutral-400 max-w-md text-sm sm:text-base"> |
|||
<div className="flex space-x-10 justify-between p-3 bg-neutral-100 dark:bg-neutral-800 rounded-lg"> |
|||
<span>Check-in</span> |
|||
<span>08:00 am - 12:00 am</span> |
|||
</div> |
|||
<div className="flex space-x-10 justify-between p-3"> |
|||
<span>Check-out</span> |
|||
<span>02:00 pm - 04:00 pm</span> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
<div className="w-14 border-b border-neutral-200 dark:border-neutral-700" /> |
|||
|
|||
{/* CONTENT */} |
|||
<div> |
|||
<h4 className="text-lg font-semibold">Special Note</h4> |
|||
<div className="prose sm:prose"> |
|||
<ul className="mt-3 text-neutral-500 dark:text-neutral-400 space-y-2"> |
|||
<li> |
|||
Ban and I will work together to keep the landscape and |
|||
environment green and clean by not littering, not using |
|||
stimulants and respecting people around. |
|||
</li> |
|||
<li>Do not sing karaoke past 11:30</li> |
|||
</ul> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
); |
|||
}; |
|||
|
|||
const renderSidebar = () => { |
|||
return ( |
|||
<div className="listingSectionSidebar__wrap shadow-xl"> |
|||
{/* PRICE */} |
|||
<div className="flex justify-between"> |
|||
<span className="text-3xl font-semibold"> |
|||
$119 |
|||
<span className="ml-1 text-base font-normal text-neutral-500 dark:text-neutral-400"> |
|||
/night |
|||
</span> |
|||
</span> |
|||
<StartRating /> |
|||
</div> |
|||
|
|||
{/* FORM */} |
|||
<form className="flex flex-col border border-neutral-200 dark:border-neutral-700 rounded-3xl "> |
|||
<StayDatesRangeInput className="flex-1 z-[11]" /> |
|||
<div className="w-full border-b border-neutral-200 dark:border-neutral-700"></div> |
|||
<GuestsInput className="flex-1" /> |
|||
</form> |
|||
|
|||
{/* SUM */} |
|||
<div className="flex flex-col space-y-4"> |
|||
<div className="flex justify-between text-neutral-6000 dark:text-neutral-300"> |
|||
<span>$119 x 3 night</span> |
|||
<span>$357</span> |
|||
</div> |
|||
<div className="flex justify-between text-neutral-6000 dark:text-neutral-300"> |
|||
<span>Service charge</span> |
|||
<span>$0</span> |
|||
</div> |
|||
<div className="border-b border-neutral-200 dark:border-neutral-700"></div> |
|||
<div className="flex justify-between font-semibold"> |
|||
<span>Total</span> |
|||
<span>$199</span> |
|||
</div> |
|||
</div> |
|||
|
|||
{/* SUBMIT */} |
|||
<ButtonPrimary href={"/checkout"}>Reserve</ButtonPrimary> |
|||
</div> |
|||
); |
|||
}; |
|||
|
|||
return ( |
|||
<div className="nc-ListingStayDetailPage"> |
|||
{/* HEADER */} |
|||
<header className="rounded-md sm:rounded-xl"> |
|||
<div className="relative grid grid-cols-3 sm:grid-cols-4 gap-1 sm:gap-2"> |
|||
<div |
|||
className="col-span-2 row-span-3 sm:row-span-2 relative rounded-md sm:rounded-xl overflow-hidden cursor-pointer" |
|||
onClick={handleOpenModalImageGallery} |
|||
> |
|||
<Image |
|||
fill |
|||
className="object-cover rounded-md sm:rounded-xl" |
|||
src={PHOTOS[0]} |
|||
alt="" |
|||
sizes="(max-width: 768px) 100vw, (max-width: 1200px) 50vw, 50vw" |
|||
/> |
|||
<div className="absolute inset-0 bg-neutral-900 bg-opacity-20 opacity-0 hover:opacity-100 transition-opacity"></div> |
|||
</div> |
|||
{PHOTOS.filter((_, i) => i >= 1 && i < 5).map((item, index) => ( |
|||
<div |
|||
key={index} |
|||
className={`relative rounded-md sm:rounded-xl overflow-hidden ${ |
|||
index >= 3 ? "hidden sm:block" : "" |
|||
}`}
|
|||
> |
|||
<div className="aspect-w-4 aspect-h-3 sm:aspect-w-6 sm:aspect-h-5"> |
|||
<Image |
|||
fill |
|||
className="object-cover rounded-md sm:rounded-xl " |
|||
src={item || ""} |
|||
alt="" |
|||
sizes="400px" |
|||
/> |
|||
</div> |
|||
|
|||
{/* OVERLAY */} |
|||
<div |
|||
className="absolute inset-0 bg-neutral-900 bg-opacity-20 opacity-0 hover:opacity-100 transition-opacity cursor-pointer" |
|||
onClick={handleOpenModalImageGallery} |
|||
/> |
|||
</div> |
|||
))} |
|||
|
|||
<button |
|||
className="absolute hidden md:flex md:items-center md:justify-center left-3 bottom-3 px-4 py-2 rounded-xl bg-neutral-100 text-neutral-500 hover:bg-neutral-200 z-10" |
|||
onClick={handleOpenModalImageGallery} |
|||
> |
|||
<Squares2X2Icon className="w-5 h-5" /> |
|||
<span className="ml-2 text-neutral-800 text-sm font-medium"> |
|||
Show all photos |
|||
</span> |
|||
</button> |
|||
</div> |
|||
</header> |
|||
|
|||
{/* MAIN */} |
|||
<main className=" relative z-10 mt-11 flex flex-col lg:flex-row "> |
|||
{/* CONTENT */} |
|||
<div className="w-full lg:w-3/5 xl:w-2/3 space-y-8 lg:space-y-10 lg:pr-10"> |
|||
{renderSection1()} |
|||
{renderSection2()} |
|||
{renderSection3()} |
|||
{renderSection4()} |
|||
<SectionDateRange /> |
|||
{renderSection5()} |
|||
{renderSection6()} |
|||
{renderSection7()} |
|||
{renderSection8()} |
|||
</div> |
|||
|
|||
{/* SIDEBAR */} |
|||
<div className="hidden lg:block flex-grow mt-14 lg:mt-0"> |
|||
<div className="sticky top-28">{renderSidebar()}</div> |
|||
</div> |
|||
</main> |
|||
</div> |
|||
); |
|||
}; |
|||
|
|||
export default ListingStayDetailPage; |
@ -1,45 +0,0 @@ |
|||
import React, { FC } from "react"; |
|||
import imagePng from "@/images/hero-right-3.png"; |
|||
import Image from "next/image"; |
|||
import HeroRealEstateSearchForm from "../(client-components)/(HeroSearchForm)/(real-estate-search-form)/HeroRealEstateSearchForm"; |
|||
|
|||
export interface SectionHero2ArchivePageProps { |
|||
className?: string; |
|||
} |
|||
|
|||
const SectionHero2ArchivePage: FC<SectionHero2ArchivePageProps> = ({ |
|||
className = "", |
|||
}) => { |
|||
return ( |
|||
<div |
|||
className={`nc-SectionHero2ArchivePage relative ${className}`} |
|||
data-nc-id="SectionHero2ArchivePage" |
|||
> |
|||
<div className="absolute inset-y-0 w-5/6 xl:w-3/4 right-0 flex-grow"> |
|||
<Image fill className="object-cover" src={imagePng} alt="hero" /> |
|||
</div> |
|||
<div className="relative py-14 "> |
|||
<div className="relative inline-flex"> |
|||
<div className="w-screen right-10 md:right-32 inset-y-0 absolute bg-primary-500"></div> |
|||
<div className="relative max-w-3xl inline-flex flex-shrink-0 flex-col items-start py-16 sm:py-20 space-y-8 sm:space-y-10 text-white"> |
|||
<h2 className="font-medium text-4xl md:text-5xl xl:text-7xl leading-[110%]"> |
|||
Tokyo, Jappan |
|||
</h2> |
|||
<div className="flex items-center text-base md:text-lg "> |
|||
<i className="text-2xl las la-map-marked"></i> |
|||
<span className="ml-2.5">Jappan </span> |
|||
<span className="mx-5"></span> |
|||
<i className="text-2xl las la-home"></i> |
|||
<span className="ml-2.5">112 properties</span> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
<div className="hidden lg:block mt-10 w-full"> |
|||
<HeroRealEstateSearchForm /> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
); |
|||
}; |
|||
|
|||
export default SectionHero2ArchivePage; |
Write
Preview
Loading…
Cancel
Save
Reference in new issue