Browse Source

coustom tour , tour history added

main
sina_sajjadi 2 months ago
parent
commit
2aac451394
  1. 120
      package-lock.json
  2. 1
      package.json
  3. 7
      src/app/(client-components)/(Header)/MainNav1.tsx
  4. 65
      src/app/(stay-listings)/SectionGridFilterCard.tsx
  5. 21
      src/app/(stay-listings)/SectionGridHasMap.tsx
  6. 155
      src/app/(stay-listings)/TabFilters.tsx
  7. 6
      src/app/(stay-listings)/layout.tsx
  8. 3
      src/app/(stay-listings)/listing-stay-map/page.tsx
  9. 111
      src/app/custom-history/page.tsx
  10. 340
      src/app/custom-trip/page.tsx
  11. 3
      src/app/layout.tsx
  12. 83
      src/app/tours/SectionGridFilterCard.tsx
  13. 690
      src/app/tours/TabFilters.tsx
  14. 14
      src/app/tours/page.tsx
  15. 2
      src/components/CardCategory3.tsx
  16. 9
      src/components/SectionGridFeaturePlaces.tsx
  17. 20
      src/shared/Checkbox.tsx
  18. 85
      yarn.lock

120
package-lock.json

@ -26,7 +26,6 @@
"google-map-react": "^2.2.1",
"lodash": "^4.17.21",
"next": "^13.4.3",
"next-auth": "^4.24.7",
"rc-slider": "^10.1.1",
"react": "^18.2.0",
"react-datepicker": "^4.11.0",
@ -476,14 +475,6 @@
"node": ">= 8"
}
},
"node_modules/@panva/hkdf": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/@panva/hkdf/-/hkdf-1.1.1.tgz",
"integrity": "sha512-dhPeilub1NuIG0X5Kvhh9lH4iW3ZsHlnzwgwbOlgwQ2wG1IqFzsgHqmKPk3WzsdWAeaxKJxgM0+W433RmN45GA==",
"funding": {
"url": "https://github.com/sponsors/panva"
}
},
"node_modules/@pkgr/utils": {
"version": "2.4.1",
"resolved": "https://registry.npmjs.org/@pkgr/utils/-/utils-2.4.1.tgz",
@ -1339,14 +1330,6 @@
"integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==",
"license": "MIT"
},
"node_modules/cookie": {
"version": "0.5.0",
"resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz",
"integrity": "sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==",
"engines": {
"node": ">= 0.6"
}
},
"node_modules/copy-to-clipboard": {
"version": "3.3.3",
"resolved": "https://registry.npmjs.org/copy-to-clipboard/-/copy-to-clipboard-3.3.3.tgz",
@ -3179,14 +3162,6 @@
"jiti": "bin/jiti.js"
}
},
"node_modules/jose": {
"version": "4.15.9",
"resolved": "https://registry.npmjs.org/jose/-/jose-4.15.9.tgz",
"integrity": "sha512-1vUQX+IdDMVPj4k8kOxgUqlcK518yluMuGZwqlr44FS1ppZB/5GWh4rZG89erpOBOJjU/OBsnCVFfapsRz6nEA==",
"funding": {
"url": "https://github.com/sponsors/panva"
}
},
"node_modules/js-cookie": {
"version": "2.2.1",
"resolved": "https://registry.npmjs.org/js-cookie/-/js-cookie-2.2.1.tgz",
@ -3564,33 +3539,6 @@
}
}
},
"node_modules/next-auth": {
"version": "4.24.7",
"resolved": "https://registry.npmjs.org/next-auth/-/next-auth-4.24.7.tgz",
"integrity": "sha512-iChjE8ov/1K/z98gdKbn2Jw+2vLgJtVV39X+rCP5SGnVQuco7QOr19FRNGMIrD8d3LYhHWV9j9sKLzq1aDWWQQ==",
"dependencies": {
"@babel/runtime": "^7.20.13",
"@panva/hkdf": "^1.0.2",
"cookie": "^0.5.0",
"jose": "^4.15.5",
"oauth": "^0.9.15",
"openid-client": "^5.4.0",
"preact": "^10.6.3",
"preact-render-to-string": "^5.1.19",
"uuid": "^8.3.2"
},
"peerDependencies": {
"next": "^12.2.5 || ^13 || ^14",
"nodemailer": "^6.6.5",
"react": "^17.0.2 || ^18",
"react-dom": "^17.0.2 || ^18"
},
"peerDependenciesMeta": {
"nodemailer": {
"optional": true
}
}
},
"node_modules/next/node_modules/postcss": {
"version": "8.4.14",
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.14.tgz",
@ -3668,11 +3616,6 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/oauth": {
"version": "0.9.15",
"resolved": "https://registry.npmjs.org/oauth/-/oauth-0.9.15.tgz",
"integrity": "sha512-a5ERWK1kh38ExDEfoO6qUHJb32rd7aYmPHuyCu3Fta/cnICvYmgd2uhuKXvPD+PXB+gCEYYEaQdIRAjCOwAKNA=="
},
"node_modules/object-assign": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
@ -3804,14 +3747,6 @@
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/oidc-token-hash": {
"version": "5.0.3",
"resolved": "https://registry.npmjs.org/oidc-token-hash/-/oidc-token-hash-5.0.3.tgz",
"integrity": "sha512-IF4PcGgzAr6XXSff26Sk/+P4KZFJVuHAJZj3wgO3vX2bMdNVp/QXTP3P7CEm9V1IdG8lDLY3HhiqpsE/nOwpPw==",
"engines": {
"node": "^10.13.0 || >=12.0.0"
}
},
"node_modules/once": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
@ -3854,28 +3789,6 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/openid-client": {
"version": "5.5.0",
"resolved": "https://registry.npmjs.org/openid-client/-/openid-client-5.5.0.tgz",
"integrity": "sha512-Y7Xl8BgsrkzWLHkVDYuroM67hi96xITyEDSkmWaGUiNX6CkcXC3XyQGdv5aWZ6dukVKBFVQCADi9gCavOmU14w==",
"dependencies": {
"jose": "^4.14.4",
"lru-cache": "^6.0.0",
"object-hash": "^2.2.0",
"oidc-token-hash": "^5.0.3"
},
"funding": {
"url": "https://github.com/sponsors/panva"
}
},
"node_modules/openid-client/node_modules/object-hash": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/object-hash/-/object-hash-2.2.0.tgz",
"integrity": "sha512-gScRMn0bS5fH+IuwyIFgnh9zBdo4DV+6GhygmWM9HyNJSgS0hScp1f5vjtm7oIIOiT9trXrShAkLFSc2IqKNgw==",
"engines": {
"node": ">= 6"
}
},
"node_modules/optionator": {
"version": "0.9.1",
"resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz",
@ -4144,26 +4057,6 @@
"integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==",
"license": "MIT"
},
"node_modules/preact": {
"version": "10.17.1",
"resolved": "https://registry.npmjs.org/preact/-/preact-10.17.1.tgz",
"integrity": "sha512-X9BODrvQ4Ekwv9GURm9AKAGaomqXmip7NQTZgY7gcNmr7XE83adOMJvd3N42id1tMFU7ojiynRsYnY6/BRFxLA==",
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/preact"
}
},
"node_modules/preact-render-to-string": {
"version": "5.2.6",
"resolved": "https://registry.npmjs.org/preact-render-to-string/-/preact-render-to-string-5.2.6.tgz",
"integrity": "sha512-JyhErpYOvBV1hEPwIxc/fHWXPfnEGdRKxc8gFdAZ7XV4tlzyzG847XAyEZqoDnynP88akM4eaHcSOzNcLWFguw==",
"dependencies": {
"pretty-format": "^3.8.0"
},
"peerDependencies": {
"preact": ">=10"
}
},
"node_modules/prelude-ls": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz",
@ -4173,11 +4066,6 @@
"node": ">= 0.8.0"
}
},
"node_modules/pretty-format": {
"version": "3.8.0",
"resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-3.8.0.tgz",
"integrity": "sha512-WuxUnVtlWL1OfZFQFuqvnvs6MiAGk9UNsBostyBOB0Is9wb5uRESevA6rnl/rkksXaGX3GzZhPup5d6Vp1nFew=="
},
"node_modules/prop-types": {
"version": "15.8.1",
"resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz",
@ -5402,14 +5290,6 @@
"integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==",
"license": "MIT"
},
"node_modules/uuid": {
"version": "8.3.2",
"resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz",
"integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==",
"bin": {
"uuid": "dist/bin/uuid"
}
},
"node_modules/warning": {
"version": "4.0.3",
"resolved": "https://registry.npmjs.org/warning/-/warning-4.0.3.tgz",

1
package.json

@ -27,7 +27,6 @@
"google-map-react": "^2.2.1",
"lodash": "^4.17.21",
"next": "^13.4.3",
"next-auth": "^4.24.7",
"rc-slider": "^10.1.1",
"react": "^18.2.0",
"react-datepicker": "^4.11.0",

7
src/app/(client-components)/(Header)/MainNav1.tsx

@ -1,3 +1,5 @@
"use client"
import React, { FC, useContext, useEffect, useState } from "react";
import Logo from "@/shared/Logo";
import Navigation from "@/shared/Navigation/Navigation";
@ -19,7 +21,12 @@ const MainNav1: FC<MainNav1Props> = ({ className = "" }) => {
const { status, setStatus } = useContext(user);
const User = JSON.parse(localStorage.getItem("user"))
useEffect(()=>{
User? setStatus(true) : setStatus(false)
} , [])
return (
<div className={`nc-MainNav1 relative z-10 ${className}`}>
<div className="px-4 lg:container h-20 relative flex justify-between">

65
src/app/(stay-listings)/SectionGridFilterCard.tsx

@ -1,39 +1,72 @@
import React, { FC } from "react";
"use client";
import React, { FC, useContext, useEffect, useState } from "react";
import { DEMO_STAY_LISTINGS } from "@/data/listings";
import { StayDataType } from "@/data/types";
import Pagination from "@/shared/Pagination";
import TabFilters from "./TabFilters";
import Heading2 from "@/shared/Heading2";
import StayCard2 from "@/components/StayCard2";
import { Context } from "@/components/contexts/tourDetails";
import { useParams, useSearchParams } from "next/navigation";
import { useRouter } from "next/navigation";
export interface SectionGridFilterCardProps {
className?: string;
data?: StayDataType[];
}
const DEMO_DATA: StayDataType[] = DEMO_STAY_LISTINGS.filter((_, i) => i < 8);
const SectionGridFilterCard: FC<SectionGridFilterCardProps> = ({
className = "",
data = DEMO_DATA,
data = DEMO_STAY_LISTINGS,
}) => {
const { countries, tours } = useContext(Context);
const [countryTours, setCountryTours] = useState(tours.results || []);
const [checked, setChecked] = useState<{ [key: string]: boolean }>({});
const searchParams = useSearchParams()
console.log(searchParams);
// Get the list of selected countries
const filteredCountries = Object.keys(checked).filter((countryName) => checked[countryName]);
console.log(searchParams.get("country"));
useEffect(() => {
if (!tours.results) return;
if (filteredCountries.length === 0) {
// If no country is selected, show all tours
setCountryTours(tours.results);
} else {
// Map selected country names to country codes
const selectedCountryCodes = countries
.filter((country) => filteredCountries.includes(country.name))
.map((country) => country.code);
// Filter tours based on selected country codes
const filteredTours = tours.results.filter((tour) =>
selectedCountryCodes.includes(tour.destination_country)
);
setCountryTours(filteredTours);
}
}, [checked, countries, tours.results]);
return (
<div
className={`nc-SectionGridFilterCard ${className}`}
data-nc-id="SectionGridFilterCard"
>
<div className={`nc-SectionGridFilterCard container ${className}`} data-nc-id="SectionGridFilterCard">
<Heading2 />
<div className="mb-8 lg:mb-11">
<TabFilters />
<TabFilters onChangeCountry={setChecked} data={countries} />
</div>
<div className="grid grid-cols-1 gap-6 md:gap-8 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4">
{data.map((stay) => (
<StayCard2 key={stay.id} data={stay} />
))}
</div>
<div className="flex mt-16 justify-center items-center">
<Pagination />
{countryTours.length > 0 ? (
countryTours.map((stay) => <StayCard2 key={stay.id} data={stay} />)
) : (
<h2>No tours Available</h2>
)}
</div>
</div>
);

21
src/app/(stay-listings)/SectionGridHasMap.tsx

@ -23,7 +23,7 @@ const SectionGridHasMap: FC<SectionGridHasMapProps> = () => {
<div className="relative flex min-h-screen">
{/* CARDSSSS */}
<div className="min-h-screen w-full xl:w-[60%] 2xl:w-[60%] max-w-[1184px] flex-shrink-0 xl:px-8 ">
<Heading2 className="!mb-8" />
{/* <Heading2 className="!mb-8" /> */}
<div className="mb-8 lg:mb-11">
<TabFilters />
</div>
@ -74,24 +74,7 @@ const SectionGridHasMap: FC<SectionGridHasMapProps> = () => {
label="Search as I move the map"
/>
</div>
<GoogleMapReact
defaultZoom={12}
defaultCenter={DEMO_STAYS[0].map}
bootstrapURLKeys={{
key: "AIzaSyAGVJfZMAKYfZ71nzL_v5i3LjTTWnCYwTY",
}}
yesIWantToUseGoogleMapApiInternals
>
{DEMO_STAYS.map((item) => (
<AnyReactComponent
isSelected={currentHoverID === item.id}
key={item.id}
lat={item.map.lat}
lng={item.map.lng}
listing={item}
/>
))}
</GoogleMapReact>
</div>
</div>
</div>

155
src/app/(stay-listings)/TabFilters.tsx

@ -1,6 +1,6 @@
"use client";
import React, { Fragment, useState } from "react";
import React, { Fragment, useEffect, useState } from "react";
import { Dialog, Popover, Transition } from "@headlessui/react";
import NcInputNumber from "@/components/NcInputNumber";
import ButtonPrimary from "@/shared/ButtonPrimary";
@ -67,10 +67,18 @@ const moreFilter3 = [
const moreFilter4 = [{ name: " Pets allowed" }, { name: "Smoking allowed" }];
const TabFilters = () => {
const TabFilters = ({data ,
onChangeCountry = (item)=>{}
}) => {
const [isOpenMoreFilter, setisOpenMoreFilter] = useState(false);
const [isOpenMoreFilterMobile, setisOpenMoreFilterMobile] = useState(false);
const [rangePrices, setRangePrices] = useState([0, 1000]);
const [checkedItems, setCheckedItems] = useState({});
useEffect(()=>{
onChangeCountry(checkedItems)
} , [checkedItems])
//
const closeModalMoreFilter = () => setisOpenMoreFilter(false);
@ -78,6 +86,7 @@ const TabFilters = () => {
//
const closeModalMoreFilterMobile = () => setisOpenMoreFilterMobile(false);
const openModalMoreFilterMobile = () => setisOpenMoreFilterMobile(true);
console.log(data);
const renderXClear = () => {
return (
@ -113,37 +122,27 @@ const TabFilters = () => {
</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"
// ... transition props
>
<Popover.Panel className="absolute z-10 w-screen max-w-sm px-4 mt-3 left-0 sm:px-0 lg:max-w-md">
<div className="overflow-hidden rounded-2xl shadow-xl bg-white dark:bg-neutral-900 border border-neutral-200 dark:border-neutral-700">
<div className="relative flex flex-col px-5 py-6 space-y-5">
{typeOfPaces.map((item) => (
<div key={item.name} className="">
{data.map((item) => (
<div key={item.id} className="">
<Checkbox
name={item.name}
label={item.name}
subLabel={item.description}
checked={!!checkedItems[item.name]}
onChange={(checked) => {
setCheckedItems((prev) => ({
...prev,
[item.name]: checked,
}));
}}
/>
</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>
@ -311,39 +310,53 @@ const TabFilters = () => {
);
};
const renderMoreFilterItem = (
data: {
name: string;
defaultChecked?: boolean;
}[]
) => {
const list1 = data.filter((_, i) => i < data.length / 2);
const list2 = data.filter((_, i) => i >= data.length / 2);
return (
<div className="grid grid-cols-2 gap-8">
<div className="flex flex-col space-y-5">
{list1.map((item) => (
<Checkbox
key={item.name}
name={item.name}
label={item.name}
defaultChecked={!!item.defaultChecked}
/>
))}
</div>
<div className="flex flex-col space-y-5">
{list2.map((item) => (
<Checkbox
key={item.name}
name={item.name}
label={item.name}
defaultChecked={!!item.defaultChecked}
/>
))}
</div>
// Inside TabFilters component
const renderMoreFilterItem = (
data: {
name: string;
defaultChecked?: boolean;
}[]
) => {
const list1 = data.filter((_, i) => i < data.length / 2);
const list2 = data.filter((_, i) => i >= data.length / 2);
return (
<div className="grid grid-cols-2 gap-8">
<div className="flex flex-col space-y-5">
{list1.map((item) => (
<Checkbox
key={item.name}
name={item.name}
label={item.name}
checked={!!checkedItems[item.name]}
onChange={(checked) => {
setCheckedItems((prev) => ({
...prev,
[item.name]: checked,
}));
}}
/>
))}
</div>
);
};
<div className="flex flex-col space-y-5">
{list2.map((item) => (
<Checkbox
key={item.name}
name={item.name}
label={item.name}
checked={!!checkedItems[item.name]}
onChange={(checked) => {
setCheckedItems((prev) => ({
...prev,
[item.name]: checked,
}));
}}
/>
))}
</div>
</div>
);
};
const renderTabMoreFilter = () => {
return (
@ -463,7 +476,7 @@ const TabFilters = () => {
className={`flex lg:hidden items-center justify-center px-4 py-2 text-sm rounded-full border border-primary-500 bg-primary-50 text-primary-700 focus:outline-none cursor-pointer`}
onClick={openModalMoreFilterMobile}
>
<span>More filters (3)</span>
<span>Countries</span>
{renderXClear()}
</div>
@ -521,12 +534,12 @@ const TabFilters = () => {
<div className="py-7">
<h3 className="text-xl font-medium">Type of place</h3>
<div className="mt-6 relative ">
{renderMoreFilterItem(typeOfPaces)}
{renderMoreFilterItem(data)}
</div>
</div>
{/* ---- */}
<div className="py-7">
{/* <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">
@ -592,49 +605,49 @@ const TabFilters = () => {
</div>
</div>
</div>
</div>
</div> */}
{/* ---- */}
<div className="py-7">
{/* <div className="py-7">
<h3 className="text-xl font-medium">Rooms and beds</h3>
<div className="mt-6 relative flex flex-col space-y-5">
<NcInputNumber label="Beds" max={10} />
<NcInputNumber label="Bedrooms" max={10} />
<NcInputNumber label="Bathrooms" max={10} />
</div>
</div>
</div> */}
{/* ---- */}
<div className="py-7">
{/* <div className="py-7">
<h3 className="text-xl font-medium">Amenities</h3>
<div className="mt-6 relative ">
{renderMoreFilterItem(moreFilter1)}
</div>
</div>
</div> */}
{/* ---- */}
<div className="py-7">
{/* <div className="py-7">
<h3 className="text-xl font-medium">Facilities</h3>
<div className="mt-6 relative ">
{renderMoreFilterItem(moreFilter2)}
</div>
</div>
</div> */}
{/* ---- */}
<div className="py-7">
{/* <div className="py-7">
<h3 className="text-xl font-medium">Property type</h3>
<div className="mt-6 relative ">
{renderMoreFilterItem(moreFilter3)}
</div>
</div>
</div> */}
{/* ---- */}
<div className="py-7">
{/* <div className="py-7">
<h3 className="text-xl font-medium">House rules</h3>
<div className="mt-6 relative ">
{renderMoreFilterItem(moreFilter4)}
</div>
</div>
</div> */}
</div>
</div>
@ -665,9 +678,9 @@ const TabFilters = () => {
<div className="flex lg:space-x-4">
<div className="hidden lg:flex space-x-4">
{renderTabsTypeOfPlace()}
{renderTabsPriceRage()}
{renderTabsRoomAndBeds()}
{renderTabMoreFilter()}
{/* {renderTabsPriceRage()} */}
{/* {renderTabsRoomAndBeds()} */}
{/* {renderTabMoreFilter()} */}
</div>
{renderTabMoreFilterMobile()}
</div>

6
src/app/(stay-listings)/layout.tsx

@ -5,6 +5,7 @@ import SectionSliderNewCategories from "@/components/SectionSliderNewCategories"
import SectionSubscribe2 from "@/components/SectionSubscribe2";
import React, { ReactNode } from "react";
import SectionHeroArchivePage from "../(server-components)/SectionHeroArchivePage";
import SectionHero from "../(server-components)/SectionHero";
const Layout = ({ children }: { children: ReactNode }) => {
return (
@ -13,7 +14,7 @@ const Layout = ({ children }: { children: ReactNode }) => {
{/* SECTION HERO */}
<div className="container pt-10 pb-24 lg:pt-16 lg:pb-28">
<SectionHeroArchivePage currentPage="Stays" currentTab="Stays" />
<SectionHero className="pt-10 lg:pt-16 lg:pb-16" />
</div>
{children}
@ -32,12 +33,9 @@ const Layout = ({ children }: { children: ReactNode }) => {
</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>

3
src/app/(stay-listings)/listing-stay-map/page.tsx

@ -1,12 +1,13 @@
import React, { FC } from "react";
import SectionGridHasMap from "../SectionGridHasMap";
import SectionGridFilterCard from "../SectionGridFilterCard";
export interface ListingStayMapPageProps {}
const ListingStayMapPage: FC<ListingStayMapPageProps> = ({}) => {
return (
<div className="container pb-24 lg:pb-28 2xl:pl-10 xl:pr-0 xl:max-w-none">
<SectionGridHasMap />
<SectionGridFilterCard />
</div>
);
};

111
src/app/custom-history/page.tsx

@ -0,0 +1,111 @@
'use client';
import React, { FC, useEffect, useState } from 'react';
import axiosInstance from '@/components/api/axios';
import { TrashIcon } from '@heroicons/react/24/outline'; // Import the Trash icon for the delete button
import { FaWhatsapp } from "react-icons/fa";
export interface PageAddListing10Props {}
const PageAddListing10: FC<PageAddListing10Props> = () => {
const [tours, setTours] = useState([]);
const [toursDetail, setToursDetail] = useState([]);
const [user, setUser] = useState(null);
useEffect(() => {
const userData = localStorage.getItem('user');
if (userData) {
const parsedUser = JSON.parse(userData);
setUser(parsedUser);
axiosInstance('/api/trip/custom/orders/', {
headers: {
Authorization: `token ${parsedUser.token}`,
},
})
.then((response) => {
setTours(response.data.results);
console.log(response);
// Parse tour details
const details = response.data.results.map((item) =>
JSON.parse(item.detail)
);
setToursDetail(details);
})
.catch((error) => {
console.log(error);
});
}
}, []);
const deleteTour = (tour)=>{
const selected = tours.find((item)=>{
return JSON.parse(item.detail) === tour
})
console.log(JSON.parse(tours[0].detail ) == tour.title[1].title ,JSON.parse(tours[0].detail ) , tour)
}
console.log(tours);
return (
<div className="px-4 max-w-md mx-auto pb-24 pt-14 sm:py-24 lg:pb-32">
<div className="flex items-center justify-between mb-4">
<h2 className="text-2xl font-semibold">Custom Trip History</h2>
<span className="text-gray-500 text-sm">2023 Dec 02 | 16:17</span>
</div>
<p className="text-green-600 text-sm mb-4">Successfully Registered</p>
{toursDetail.length > 0 ? (
toursDetail.map((item, index) => (
<div
key={index}
className="bg-white border border-gray-200 rounded-3xl p-4 mb-4 shadow-md"
>
<div className="flex flex-col mb-2">
<div className='flex justify-between'>
<p className="font-semibold text-gray-700">Origin:</p>
<p className="text-gray-900">{item['1']?.city || 'N/A'}</p>
</div>
{Object.keys(item)
.filter((key) => !isNaN(Number(key)) && Number(key) > 1)
.map((key) => (
<div className='flex justify-between' key={key}>
<p className="font-semibold text-gray-700">{item[key].title}:</p>
<p className="text-gray-900">{item[key]?.city || 'N/A'}</p>
</div>
))}
</div>
<div className="flex items-center justify-between">
<div>
<p className=" text-gray-600">Estimated Cost: <span className="">${item.summary?.cost_estimate || 'N/A'}</span></p>
</div>
<button onClick={()=>{deleteTour(item)}} className="flex items-center text-red-500 hover:text-red-700">
<TrashIcon className="w-5 h-5 mr-1" />
Delete
{console.log(item)
}
</button>
</div>
</div>
))
) : (
<p className="text-center text-gray-500">No tours available.</p>
)}
<div className="flex justify-between mt-8 items-center">
<p className="font-semibold text-gray-700 mb-2">Contact Support</p>
<button className="flex items-center justify-center bg-[#032935] rounded-full text-white p-3">
<FaWhatsapp className='w-9' />
Whatsapp
</button>
</div>
</div>
);
};
export default PageAddListing10;

340
src/app/custom-trip/page.tsx

@ -1,12 +1,12 @@
"use client";
import React, { useEffect, useState } from "react";
import { FC } from "react";
import React, { FC, useEffect, useState } from "react";
import axiosInstance from "@/components/api/axios";
import ButtonPrimary from "@/shared/ButtonPrimary";
import FormItem from "../add-listing/FormItem";
import axiosInstance from "@/components/api/axios";
import Select from "@/shared/Select";
import Input from "@/shared/Input";
import { useRouter } from "next/navigation";
interface City {
name: string;
@ -21,48 +21,239 @@ interface Country {
interface CommonLayoutProps {}
const CommonLayout: FC<CommonLayoutProps> = () => {
const user = JSON.parse(localStorage.getItem("user"));
const router = useRouter()
const [countries, setCountries] = useState<Country[]>([]);
const [startCity, setStartCity] = useState<string>("");
const [endCity, setEndCity] = useState<string>("");
const [startDate, setStartDate] = useState<string>("");
const [passengers, setPassengers] = useState<number>(1);
const [transport, setTransport] = useState<any[]>([]);
const [hotel, setHotel] = useState<any[]>([]);
const [selectedHotel, setSelectedHotel] = useState<string>("");
const [selectedTransport, setSelectedTransport] = useState<string>("");
const [destinations, setDestinations] = useState<{ endCity: string, transport: string, hotel: string }[]>([]);
const [estimatedCost, setEstimatedCost] = useState<number>(0);
const [destinations, setDestinations] = useState<
{
endCity: string;
transport: string;
hotel: string;
duration: number;
finishDate: string;
transportCost: number;
hotelCost: number;
}[]
>([]);
const [isFormValid, setIsFormValid] = useState<boolean>(false);
var special = [
"Zeroth",
"First",
"Second",
"Third",
"Fourth",
"Fifth",
"Sixth",
"Seventh",
"Eighth",
"Ninth",
"Tenth",
"Eleventh",
"Twelfth",
"Thirteenth",
"Fourteenth",
"Fifteenth",
"Sixteenth",
"Seventeenth",
"Eighteenth",
"Nineteenth",
];
var deca = [
"Twent",
"Thirt",
"Fort",
"Fift",
"Sixt",
"Sevent",
"Eight",
"Ninet",
];
function stringifyNumber(n) {
if (n < 20) return special[n];
if (n % 10 === 0) return deca[Math.floor(n / 10) - 2] + "ieth";
return deca[Math.floor(n / 10) - 2] + "y-" + special[n % 10];
}
useEffect(() => {
axiosInstance.get("/api/cityguide/countries/?service=custom_trip")
axiosInstance
.get("/api/cityguide/countries/?service=custom_trip")
.then((response) => setCountries(response.data.results))
.catch((error) => console.error(error));
.catch((error) => console.error("Error fetching countries:", error));
}, []);
useEffect(() => {
console.log(destinations[destinations.length-1]?.endCity);
if (destinations[destinations.length-1]?.endCity) {
axiosInstance.get(`/api/trip/custom/transport/?from_city=${startCity}&to_city=${destinations[destinations.length-1]?.endCity}`)
.then((response) => { console.log(response)
setTransport(response.data)})
.catch((error) => console.error(error));
axiosInstance.get(`/api/trip/hotels/${destinations[destinations.length-1]?.endCity}/`)
.then((response) =>{ console.log(response)
setHotel(response.data.results)})
.catch((error) => console.error(error));
const lastDestination = destinations[destinations.length - 1];
if (lastDestination?.endCity) {
axiosInstance
.get(
`/api/trip/custom/transport/?from_city=${startCity.split("/")[2].trim()}&to_city=${lastDestination.endCity.split("/")[2].trim()}`
)
.then((response) => setTransport(response.data))
.catch((error) =>
console.error("Error fetching transport options:", error)
);
axiosInstance
.get(`/api/trip/hotels/${lastDestination.endCity.split("/")[2].trim()}/`)
.then((response) => setHotel(response.data.results))
.catch((error) => console.error("Error fetching hotels:", error));
}
}, [destinations]);
}, [destinations, startCity]);
useEffect(() => {
updateEstimatedCost();
}, [destinations, passengers]);
useEffect(() => {
validateForm();
}, [startCity, startDate, passengers, destinations]);
const updateEstimatedCost = () => {
const totalCost = destinations.reduce((acc, destination) => {
const transportCost = destination.transportCost * passengers;
const hotelCost =
destination.hotelCost * destination.duration * passengers;
return acc + transportCost + hotelCost;
}, 0);
setEstimatedCost(totalCost);
};
const validateForm = () => {
const isValid =
startCity &&
startDate &&
passengers > 0 &&
destinations.every(
(destination) =>
destination.endCity &&
destination.transport &&
destination.hotel &&
destination.duration > 0 &&
destination.finishDate
);
setIsFormValid(isValid);
};
const addDestination = () => {
setDestinations([...destinations, { endCity: "", transport: "", hotel: "" }]);
setDestinations([
...destinations,
{
endCity: "",
transport: "",
hotel: "",
duration: 1,
finishDate: "",
transportCost: 0,
hotelCost: 0,
},
]);
};
const handleDestinationChange = (index: number, field: string, value: string) => {
const handleDestinationChange = (
index: number,
field: string,
value: string | number
) => {
const updatedDestinations = [...destinations];
updatedDestinations[index] = { ...updatedDestinations[index], [field]: value };
updatedDestinations[index] = {
...updatedDestinations[index],
[field]: value,
};
if (field === "transport") {
const selected = transport.find(
(item) => item.transportaion.name === value
);
updatedDestinations[index].transportCost = selected?.price || 0;
}
if (field === "hotel") {
const selected = hotel.find((item) => item.name === value);
updatedDestinations[index].hotelCost = selected?.price_per_day || 0;
}
if (field === "duration") {
const baseDate =
index === 0 || !updatedDestinations[index - 1]?.finishDate
? new Date(startDate)
: new Date(updatedDestinations[index - 1].finishDate);
const newFinishDate = new Date(baseDate);
newFinishDate.setDate(baseDate.getDate() + Number(value));
updatedDestinations[index].finishDate = newFinishDate
.toISOString()
.split("T")[0];
}
setDestinations(updatedDestinations);
validateForm();
};
console.log(destinations);
const submitTour = async () => {
const formData = {
destinations: destinations.map((destination, index) => ({
name: `${destination.endCity.split("/")[0].trim()}-${destination.endCity.split("/")[1].trim()} `,
})),
price_estimate: JSON.stringify(estimatedCost),
detail: JSON.stringify({
"1": {
title: "Begin Trip",
city: `${startCity.split("/")[0].trim()}-${startCity.split("/")[1].trim()} `,
number_passenger: `${passengers}`,
start_date: startDate.replace(/-/g, "/"),
},
...destinations.reduce((acc, destination, index) => {
acc[index + 2] = {
title: `${stringifyNumber(index + 1)} Destination`,
city:`${destination.endCity.split("/")[0].trim()}-${destination.endCity.split("/")[1].trim()} `,
transportation: destination.transport,
hotel: destination.hotel,
duration: `${destination.duration}day`,
finish_date: destination.finishDate.replace(/-/g, "/"),
start_date: startDate.replace(/-/g, "/"), // adjust as needed
};
return acc;
}, {}),
summary: {
cost_estimate: estimatedCost.toString(),
journery_schedule: `${startDate.replace(/-/g, "/")} To ${destinations[
destinations.length - 1
]?.finishDate.replace(/-/g, "/")} Day ${destinations.length}`,
},
}),
};
try {
const response = await axiosInstance.post(
"/api/trip/custom/",
formData,
{
headers: {
Authorization: `token ${user.token}`,
"Content-Type": "application/json",
"X-CSRFToken":
"HaqCxkS63ejsvwmlmk360sbFowGtwfNS06vGDYMIfWmHTWzJdod7x0zMEeC9gBSX",
Accept: "application/json",
},
}
);
console.log("Response:", response.data);
router.push("/custom-history")
} catch (error) {
console.error("Error sending trip details:", error);
}
};
console.log(destinations);
return (
<div className="nc-PageAddListing1 px-4 max-w-3xl mx-auto pb-24 pt-14 sm:py-24 lg:pb-32">
@ -75,20 +266,22 @@ console.log(destinations);
<h3 className="text-[#BD3F3F] font-medium">Guide</h3>
<p>
First, write the origin of your departure, then choose the first
destination of your trip, the number of nights of stay and the
means of travel, then choose your travel destinations if you
wish.
destination of your trip, the number of nights of stay, and the
means of travel, then choose your travel destinations if you wish.
</p>
<div className="w-14 border-b border-neutral-200 dark:border-neutral-700"></div>
<div className="space-y-8">
<p className="text-bronze text-xs">Begin your trip</p>
<FormItem label="" desc="">
<Select value={startCity} onChange={(e) => setStartCity(e.target.value)}>
<Select
value={startCity}
onChange={(e) => setStartCity(e.target.value)}
>
<option value="">Select City</option>
{countries.flatMap((country) =>
country.city.map((city) => (
<option key={city.name} value={city.slug}>
<option key={city.name} value={`${country.name} / ${city.name} / ${city.slug}`}>
{`${country.name} - ${city.name}`}
</option>
))
@ -97,10 +290,21 @@ console.log(destinations);
</FormItem>
<div className="flex w-[100%]">
<FormItem className="w-[50%]" label="Start Date">
<Input type="date" />
<Input
type="date"
value={startDate}
onChange={(e) => setStartDate(e.target.value)}
/>
</FormItem>
<FormItem className="w-[50%] ml-4" label="Number Of Passengers">
<Input type="number" />
<Input
type="number"
value={passengers}
onChange={(e) =>
setPassengers(Math.max(1, Number(e.target.value)))
}
min="1"
/>
</FormItem>
</div>
</div>
@ -108,16 +312,18 @@ console.log(destinations);
{destinations.map((destination, index) => (
<div key={index} className="space-y-8">
<p className="text-bronze text-xs">Destination {index + 1}</p>
<p className="text-bronze text-xs">{stringifyNumber(index + 1)} Destination </p>
<FormItem label="" desc="">
<Select
value={destination.endCity}
onChange={(e) => handleDestinationChange(index, 'endCity', e.target.value)}
onChange={(e) =>
handleDestinationChange(index, "endCity", e.target.value)
}
>
<option value="">Select City</option>
{countries.flatMap((country) =>
country.city.map((city) => (
<option key={city.name} value={city.slug}>
<option key={city.name} value={`${country.name} / ${city.name} / ${city.slug}`}>
{`${country.name} - ${city.name}`}
</option>
))
@ -127,35 +333,61 @@ console.log(destinations);
<FormItem label="Transportation" desc="">
<Select
value={destination.transport}
onChange={(e) => handleDestinationChange(index, 'transport', e.target.value)}
onChange={(e) =>
handleDestinationChange(
index,
"transport",
e.target.value
)
}
>
<option value="">Select Transport</option>
{transport?.map((item) =>
<option key={item.transportaion.id} value={item.transportaion.name}>
{transport.map((item) => (
<option
key={item.transportaion.id}
value={item.transportaion.name}
>
{item.transportaion.name}
</option>
)}
))}
</Select>
</FormItem>
<FormItem label="Hotel" desc="">
<Select
value={destination.hotel}
onChange={(e) => handleDestinationChange(index, 'hotel', e.target.value)}
onChange={(e) =>
handleDestinationChange(index, "hotel", e.target.value)
}
>
<option value="">Select Hotel</option>
{hotel?.map((hotelItem) =>
{hotel.map((hotelItem) => (
<option key={hotelItem.name} value={hotelItem.name}>
{hotelItem.name}
</option>
)}
))}
</Select>
</FormItem>
<div className="flex w-[100%]">
<FormItem className="w-[50%]" label="Duration">
<Input type="number" />
<Input
type="number"
value={destination.duration}
onChange={(e) =>
handleDestinationChange(
index,
"duration",
Math.max(1, Number(e.target.value))
)
}
min="1"
/>
</FormItem>
<FormItem className="w-[50%] ml-4" label="Finish date">
<Input type="number" />
<Input
readOnly
type="date"
value={destination.finishDate}
/>
</FormItem>
</div>
</div>
@ -163,8 +395,20 @@ console.log(destinations);
</div>
<div className="flex mt-4 justify-end space-x-5">
<ButtonPrimary onClick={addDestination} type="button">Add Destination</ButtonPrimary>
<ButtonPrimary type="button">Continue</ButtonPrimary>
<ButtonPrimary
onClick={addDestination}
type="button"
disabled={!isFormValid}
>
Add Destination
</ButtonPrimary>
<ButtonPrimary
onClick={submitTour}
type="button"
disabled={!isFormValid}
>
Continue
</ButtonPrimary>
</div>
</form>
</div>

3
src/app/layout.tsx

@ -3,7 +3,6 @@
import { Poppins } from "next/font/google";
import SiteHeader from "./(client-components)/(Header)/SiteHeader";
import ClientCommons from "./ClientCommons";
import { SessionProvider } from "next-auth/react";
import { ContextProvider } from "@/components/contexts/tourDetails";
import "./globals.css";
import "@/fonts/line-awesome-1.3.0/css/line-awesome.css";
@ -29,7 +28,6 @@ export default function RootLayout({
return (
<html lang="en" className={poppins.className}>
<body className="bg-white text-base dark:bg-neutral-900 text-neutral-900 dark:text-neutral-200">
<SessionProvider>
<ContextProvider>
<UserProvider>
<ClientCommons />
@ -39,7 +37,6 @@ export default function RootLayout({
<Footer />
</UserProvider>
</ContextProvider>
</SessionProvider>
</body>
</html>
);

83
src/app/tours/SectionGridFilterCard.tsx

@ -0,0 +1,83 @@
"use client";
import React, { FC, useContext, useEffect, useState } from "react";
import { DEMO_STAY_LISTINGS } from "@/data/listings";
import { StayDataType } from "@/data/types";
import TabFilters from "./TabFilters";
import Heading2 from "@/shared/Heading2";
import StayCard2 from "@/components/StayCard2";
import { Context } from "@/components/contexts/tourDetails";
import { useParams, useSearchParams } from "next/navigation";
import { useRouter } from "next/navigation";
export interface SectionGridFilterCardProps {
className?: string;
data?: StayDataType[];
}
const SectionGridFilterCard: FC<SectionGridFilterCardProps> = ({
className = "",
data = DEMO_STAY_LISTINGS,
}) => {
const { countries, tours } = useContext(Context);
const [countryTours, setCountryTours] = useState(tours.results || []);
const [checked, setChecked] = useState<{ [key: string]: boolean }>({});
const searchParams = useSearchParams()
console.log(searchParams);
// Get the list of selected countries
const filteredCountries = Object.keys(checked).filter((countryName) => checked[countryName]);
useEffect(()=>{
const country = searchParams.get("country")
if (searchParams.has("country")){
setChecked({
[country] : true
})
}
} , [])
console.log(checked);
useEffect(() => {
if (!tours.results) return;
if (filteredCountries.length === 0) {
// If no country is selected, show all tours
setCountryTours(tours.results);
} else {
// Map selected country names to country codes
const selectedCountryCodes = countries
.filter((country) => filteredCountries.includes(country.name))
.map((country) => country.code);
// Filter tours based on selected country codes
const filteredTours = tours.results.filter((tour) =>
selectedCountryCodes.includes(tour.destination_country)
);
setCountryTours(filteredTours);
}
}, [checked, countries, tours.results]);
return (
<div className={`nc-SectionGridFilterCard container ${className}`} data-nc-id="SectionGridFilterCard">
<Heading2 />
<div className="mb-8 lg:mb-11">
<TabFilters onChangeCountry={setChecked} data={countries} />
</div>
<div className="grid grid-cols-1 gap-6 md:gap-8 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4">
{countryTours.length > 0 ? (
countryTours.map((stay) => <StayCard2 key={stay.id} data={stay} />)
) : (
<h2>No tours Available</h2>
)}
</div>
</div>
);
};
export default SectionGridFilterCard;

690
src/app/tours/TabFilters.tsx

@ -0,0 +1,690 @@
"use client";
import React, { Fragment, useEffect, useState } from "react";
import { Dialog, Popover, Transition } from "@headlessui/react";
import NcInputNumber from "@/components/NcInputNumber";
import ButtonPrimary from "@/shared/ButtonPrimary";
import ButtonThird from "@/shared/ButtonThird";
import ButtonClose from "@/shared/ButtonClose";
import Checkbox from "@/shared/Checkbox";
import Slider from "rc-slider";
import convertNumbThousand from "@/utils/convertNumbThousand";
// DEMO DATA
const typeOfPaces = [
{
name: "Entire place",
description: "Have a place to yourself",
},
{
name: "Private room",
description: "Have your own room and share some common spaces",
},
{
name: "Hotel room",
description:
"Have a private or shared room in a boutique hotel, hostel, and more",
},
{
name: "Shared room",
description: "Stay in a shared space, like a common room",
},
];
const moreFilter1 = [
{ name: "Kitchen", defaultChecked: true },
{ name: "Air conditioning", defaultChecked: true },
{ name: "Heating" },
{ name: "Dryer" },
{ name: "Washer" },
{ name: "Wifi" },
{ name: "Indoor fireplace" },
{ name: "Breakfast" },
{ name: "Hair dryer" },
{ name: " Dedicated workspace" },
];
const moreFilter2 = [
{ name: " Free parking on premise" },
{ name: "Hot tub" },
{ name: "Gym" },
{ name: " Pool" },
{ name: " EV charger" },
];
const moreFilter3 = [
{ name: " House" },
{ name: "Bed and breakfast" },
{ name: "Apartment", defaultChecked: true },
{ name: " Boutique hotel" },
{ name: " Bungalow" },
{ name: " Chalet", defaultChecked: true },
{ name: " Condominium", defaultChecked: true },
{ name: " Cottage" },
{ name: " Guest suite" },
{ name: " Guesthouse" },
];
const moreFilter4 = [{ name: " Pets allowed" }, { name: "Smoking allowed" }];
const TabFilters = ({data ,
onChangeCountry = (item)=>{}
}) => {
const [isOpenMoreFilter, setisOpenMoreFilter] = useState(false);
const [isOpenMoreFilterMobile, setisOpenMoreFilterMobile] = useState(false);
const [rangePrices, setRangePrices] = useState([0, 1000]);
const [checkedItems, setCheckedItems] = useState({});
useEffect(()=>{
onChangeCountry(checkedItems)
} , [checkedItems])
//
const closeModalMoreFilter = () => setisOpenMoreFilter(false);
const openModalMoreFilter = () => setisOpenMoreFilter(true);
//
const closeModalMoreFilterMobile = () => setisOpenMoreFilterMobile(false);
const openModalMoreFilterMobile = () => setisOpenMoreFilterMobile(true);
console.log(data);
const renderXClear = () => {
return (
<span className="w-4 h-4 rounded-full bg-primary-500 text-white flex items-center justify-center ml-3 cursor-pointer">
<svg
xmlns="http://www.w3.org/2000/svg"
className="h-3 w-3"
viewBox="0 0 20 20"
fill="currentColor"
>
<path
fillRule="evenodd"
d="M4.293 4.293a1 1 0 011.414 0L10 8.586l4.293-4.293a1 1 0 111.414 1.414L11.414 10l4.293 4.293a1 1 0 01-1.414 1.414L10 11.414l-4.293 4.293a1 1 0 01-1.414-1.414L8.586 10 4.293 5.707a1 1 0 010-1.414z"
clipRule="evenodd"
/>
</svg>
</span>
);
};
const renderTabsTypeOfPlace = () => {
return (
<Popover className="relative">
{({ open, close }) => (
<>
<Popover.Button
className={`flex items-center justify-center px-4 py-2 text-sm rounded-full border border-neutral-300 dark:border-neutral-700 hover:border-neutral-400 dark:hover:border-neutral-6000 focus:outline-none ${
open ? "!border-primary-500 " : ""
}`}
>
<span>Type of place</span>
<i className="las la-angle-down ml-2"></i>
</Popover.Button>
<Transition
as={Fragment}
// ... transition props
>
<Popover.Panel className="absolute z-10 w-screen max-w-sm px-4 mt-3 left-0 sm:px-0 lg:max-w-md">
<div className="overflow-hidden rounded-2xl shadow-xl bg-white dark:bg-neutral-900 border border-neutral-200 dark:border-neutral-700">
<div className="relative flex flex-col px-5 py-6 space-y-5">
{data.map((item) => (
<div key={item.id} className="">
<Checkbox
name={item.name}
label={item.name}
checked={!!checkedItems[item.name]}
onChange={(checked) => {
setCheckedItems((prev) => ({
...prev,
[item.name]: checked,
}));
}}
/>
</div>
))}
</div>
</div>
</Popover.Panel>
</Transition>
</>
)}
</Popover>
);
};
const renderTabsRoomAndBeds = () => {
return (
<Popover className="relative">
{({ open, close }) => (
<>
<Popover.Button
className={`flex items-center justify-center px-4 py-2 text-sm rounded-full border border-neutral-300 dark:border-neutral-700 hover:border-neutral-400 dark:hover:border-neutral-6000 focus:outline-none ${
open ? "!border-primary-500 " : ""
}`}
>
<span>Rooms of Beds</span>
<i className="las la-angle-down ml-2"></i>
</Popover.Button>
<Transition
as={Fragment}
enter="transition ease-out duration-200"
enterFrom="opacity-0 translate-y-1"
enterTo="opacity-100 translate-y-0"
leave="transition ease-in duration-150"
leaveFrom="opacity-100 translate-y-0"
leaveTo="opacity-0 translate-y-1"
>
<Popover.Panel className="absolute z-10 w-screen max-w-sm px-4 mt-3 left-0 sm:px-0 lg:max-w-md">
<div className="overflow-hidden rounded-2xl shadow-xl bg-white dark:bg-neutral-900 border border-neutral-200 dark:border-neutral-700">
<div className="relative flex flex-col px-5 py-6 space-y-5">
<NcInputNumber label="Beds" max={10} />
<NcInputNumber label="Bedrooms" max={10} />
<NcInputNumber label="Bathrooms" max={10} />
</div>
<div className="p-5 bg-neutral-50 dark:bg-neutral-900 dark:border-t dark:border-neutral-800 flex items-center justify-between">
<ButtonThird onClick={close} sizeClass="px-4 py-2 sm:px-5">
Clear
</ButtonThird>
<ButtonPrimary
onClick={close}
sizeClass="px-4 py-2 sm:px-5"
>
Apply
</ButtonPrimary>
</div>
</div>
</Popover.Panel>
</Transition>
</>
)}
</Popover>
);
};
const renderTabsPriceRage = () => {
return (
<Popover className="relative">
{({ open, close }) => (
<>
<Popover.Button
className={`flex items-center justify-center px-4 py-2 text-sm rounded-full border border-primary-500 bg-primary-50 text-primary-700 focus:outline-none `}
>
<span>
{`$${convertNumbThousand(
rangePrices[0]
)} - $${convertNumbThousand(rangePrices[1])}`}{" "}
</span>
{renderXClear()}
</Popover.Button>
<Transition
as={Fragment}
enter="transition ease-out duration-200"
enterFrom="opacity-0 translate-y-1"
enterTo="opacity-100 translate-y-0"
leave="transition ease-in duration-150"
leaveFrom="opacity-100 translate-y-0"
leaveTo="opacity-0 translate-y-1"
>
<Popover.Panel className="absolute z-10 w-screen max-w-sm px-4 mt-3 left-0 sm:px-0 ">
<div className="overflow-hidden rounded-2xl shadow-xl bg-white dark:bg-neutral-900 border border-neutral-200 dark:border-neutral-700">
<div className="relative flex flex-col px-5 py-6 space-y-8">
<div className="space-y-5">
<span className="font-medium">Price per day</span>
<Slider
range
className="text-red-400"
min={0}
max={2000}
defaultValue={[rangePrices[0], rangePrices[1]]}
allowCross={false}
onChange={(e) => setRangePrices(e as number[])}
/>
</div>
<div className="flex justify-between space-x-5">
<div>
<label
htmlFor="minPrice"
className="block text-sm font-medium text-neutral-700 dark:text-neutral-300"
>
Min price
</label>
<div className="mt-1 relative rounded-md">
<div className="absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none">
<span className="text-neutral-500 sm:text-sm">
$
</span>
</div>
<input
type="text"
name="minPrice"
disabled
id="minPrice"
className="focus:ring-indigo-500 focus:border-indigo-500 block w-full pl-7 pr-3 sm:text-sm border-neutral-200 rounded-full text-neutral-900"
value={rangePrices[0]}
/>
</div>
</div>
<div>
<label
htmlFor="maxPrice"
className="block text-sm font-medium text-neutral-700 dark:text-neutral-300"
>
Max price
</label>
<div className="mt-1 relative rounded-md">
<div className="absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none">
<span className="text-neutral-500 sm:text-sm">
$
</span>
</div>
<input
type="text"
disabled
name="maxPrice"
id="maxPrice"
className="focus:ring-indigo-500 focus:border-indigo-500 block w-full pl-7 pr-3 sm:text-sm border-neutral-200 rounded-full text-neutral-900"
value={rangePrices[1]}
/>
</div>
</div>
</div>
</div>
<div className="p-5 bg-neutral-50 dark:bg-neutral-900 dark:border-t dark:border-neutral-800 flex items-center justify-between">
<ButtonThird onClick={close} sizeClass="px-4 py-2 sm:px-5">
Clear
</ButtonThird>
<ButtonPrimary
onClick={close}
sizeClass="px-4 py-2 sm:px-5"
>
Apply
</ButtonPrimary>
</div>
</div>
</Popover.Panel>
</Transition>
</>
)}
</Popover>
);
};
// Inside TabFilters component
const renderMoreFilterItem = (
data: {
name: string;
defaultChecked?: boolean;
}[]
) => {
const list1 = data.filter((_, i) => i < data.length / 2);
const list2 = data.filter((_, i) => i >= data.length / 2);
return (
<div className="grid grid-cols-2 gap-8">
<div className="flex flex-col space-y-5">
{list1.map((item) => (
<Checkbox
key={item.name}
name={item.name}
label={item.name}
checked={!!checkedItems[item.name]}
onChange={(checked) => {
setCheckedItems((prev) => ({
...prev,
[item.name]: checked,
}));
}}
/>
))}
</div>
<div className="flex flex-col space-y-5">
{list2.map((item) => (
<Checkbox
key={item.name}
name={item.name}
label={item.name}
checked={!!checkedItems[item.name]}
onChange={(checked) => {
setCheckedItems((prev) => ({
...prev,
[item.name]: checked,
}));
}}
/>
))}
</div>
</div>
);
};
const renderTabMoreFilter = () => {
return (
<div>
<div
className={`flex items-center justify-center px-4 py-2 text-sm rounded-full border border-primary-500 bg-primary-50 text-primary-700 focus:outline-none cursor-pointer`}
onClick={openModalMoreFilter}
>
<span>More filters (3)</span>
{renderXClear()}
</div>
<Transition appear show={isOpenMoreFilter} as={Fragment}>
<Dialog
as="div"
className="fixed inset-0 z-50 overflow-y-auto"
onClose={closeModalMoreFilter}
>
<div className="min-h-screen text-center">
<Transition.Child
as={Fragment}
enter="ease-out duration-300"
enterFrom="opacity-0"
enterTo="opacity-100"
leave="ease-in duration-200"
leaveFrom="opacity-100"
leaveTo="opacity-0"
>
<Dialog.Overlay className="fixed inset-0 bg-black bg-opacity-40 dark:bg-opacity-60" />
</Transition.Child>
{/* This element is to trick the browser into centering the modal contents. */}
<span
className="inline-block h-screen align-middle"
aria-hidden="true"
>
&#8203;
</span>
<Transition.Child
className="inline-block py-8 px-2 h-screen w-full max-w-4xl"
enter="ease-out duration-300"
enterFrom="opacity-0 scale-95"
enterTo="opacity-100 scale-100"
leave="ease-in duration-200"
leaveFrom="opacity-100 scale-100"
leaveTo="opacity-0 scale-95"
>
<div className="inline-flex flex-col w-full max-w-4xl text-left align-middle transition-all transform overflow-hidden rounded-2xl bg-white dark:bg-neutral-900 dark:border dark:border-neutral-700 dark:text-neutral-100 shadow-xl h-full">
<div className="relative flex-shrink-0 px-6 py-4 border-b border-neutral-200 dark:border-neutral-800 text-center">
<Dialog.Title
as="h3"
className="text-lg font-medium leading-6 text-gray-900"
>
More filters
</Dialog.Title>
<span className="absolute left-3 top-3">
<ButtonClose onClick={closeModalMoreFilter} />
</span>
</div>
<div className="flex-grow overflow-y-auto">
<div className="px-10 divide-y divide-neutral-200 dark:divide-neutral-800">
<div className="py-7">
<h3 className="text-xl font-medium">Amenities</h3>
<div className="mt-6 relative ">
{renderMoreFilterItem(moreFilter1)}
</div>
</div>
<div className="py-7">
<h3 className="text-xl font-medium">Facilities</h3>
<div className="mt-6 relative ">
{renderMoreFilterItem(moreFilter2)}
</div>
</div>
<div className="py-7">
<h3 className="text-xl font-medium">Property type</h3>
<div className="mt-6 relative ">
{renderMoreFilterItem(moreFilter3)}
</div>
</div>
<div className="py-7">
<h3 className="text-xl font-medium">House rules</h3>
<div className="mt-6 relative ">
{renderMoreFilterItem(moreFilter4)}
</div>
</div>
</div>
</div>
<div className="p-6 flex-shrink-0 bg-neutral-50 dark:bg-neutral-900 dark:border-t dark:border-neutral-800 flex items-center justify-between">
<ButtonThird
onClick={closeModalMoreFilter}
sizeClass="px-4 py-2 sm:px-5"
>
Clear
</ButtonThird>
<ButtonPrimary
onClick={closeModalMoreFilter}
sizeClass="px-4 py-2 sm:px-5"
>
Apply
</ButtonPrimary>
</div>
</div>
</Transition.Child>
</div>
</Dialog>
</Transition>
</div>
);
};
const renderTabMoreFilterMobile = () => {
return (
<div>
<div
className={`flex lg:hidden items-center justify-center px-4 py-2 text-sm rounded-full border border-primary-500 bg-primary-50 text-primary-700 focus:outline-none cursor-pointer`}
onClick={openModalMoreFilterMobile}
>
<span>Countries</span>
{renderXClear()}
</div>
<Transition appear show={isOpenMoreFilterMobile} as={Fragment}>
<Dialog
as="div"
className="fixed inset-0 z-50 overflow-y-auto"
onClose={closeModalMoreFilterMobile}
>
<div className="min-h-screen text-center">
<Transition.Child
as={Fragment}
enter="ease-out duration-300"
enterFrom="opacity-0"
enterTo="opacity-100"
leave="ease-in duration-200"
leaveFrom="opacity-100"
leaveTo="opacity-0"
>
<Dialog.Overlay className="fixed inset-0 bg-black bg-opacity-40 dark:bg-opacity-60" />
</Transition.Child>
{/* This element is to trick the browser into centering the modal contents. */}
<span
className="inline-block h-screen align-middle"
aria-hidden="true"
>
&#8203;
</span>
<Transition.Child
className="inline-block py-8 px-2 h-screen w-full max-w-4xl"
enter="ease-out duration-300"
enterFrom="opacity-0 scale-95"
enterTo="opacity-100 scale-100"
leave="ease-in duration-200"
leaveFrom="opacity-100 scale-100"
leaveTo="opacity-0 scale-95"
>
<div className="inline-flex flex-col w-full max-w-4xl text-left align-middle transition-all transform overflow-hidden rounded-2xl bg-white dark:bg-neutral-900 dark:border dark:border-neutral-700 dark:text-neutral-100 shadow-xl h-full">
<div className="relative flex-shrink-0 px-6 py-4 border-b border-neutral-200 dark:border-neutral-800 text-center">
<Dialog.Title
as="h3"
className="text-lg font-medium leading-6 text-gray-900"
>
More filters
</Dialog.Title>
<span className="absolute left-3 top-3">
<ButtonClose onClick={closeModalMoreFilterMobile} />
</span>
</div>
<div className="flex-grow overflow-y-auto">
<div className="px-4 sm:px-6 divide-y divide-neutral-200 dark:divide-neutral-800">
{/* ---- */}
<div className="py-7">
<h3 className="text-xl font-medium">Type of place</h3>
<div className="mt-6 relative ">
{renderMoreFilterItem(data)}
</div>
</div>
{/* ---- */}
{/* <div className="py-7">
<h3 className="text-xl font-medium">Range Prices</h3>
<div className="mt-6 relative ">
<div className="relative flex flex-col space-y-8">
<div className="space-y-5">
<Slider
range
className="text-red-400"
min={0}
max={2000}
defaultValue={[0, 1000]}
allowCross={false}
onChange={(e) => setRangePrices(e as number[])}
/>
</div>
<div className="flex justify-between space-x-5">
<div>
<label
htmlFor="minPrice"
className="block text-sm font-medium text-neutral-700 dark:text-neutral-300"
>
Min price
</label>
<div className="mt-1 relative rounded-md">
<div className="absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none">
<span className="text-neutral-500 sm:text-sm">
$
</span>
</div>
<input
type="text"
name="minPrice"
disabled
id="minPrice"
className="focus:ring-indigo-500 focus:border-indigo-500 block w-full pl-7 pr-3 sm:text-sm border-neutral-200 rounded-full text-neutral-900"
value={rangePrices[0]}
/>
</div>
</div>
<div>
<label
htmlFor="maxPrice"
className="block text-sm font-medium text-neutral-700 dark:text-neutral-300"
>
Max price
</label>
<div className="mt-1 relative rounded-md">
<div className="absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none">
<span className="text-neutral-500 sm:text-sm">
$
</span>
</div>
<input
type="text"
disabled
name="maxPrice"
id="maxPrice"
className="focus:ring-indigo-500 focus:border-indigo-500 block w-full pl-7 pr-3 sm:text-sm border-neutral-200 rounded-full text-neutral-900"
value={rangePrices[1]}
/>
</div>
</div>
</div>
</div>
</div>
</div> */}
{/* ---- */}
{/* <div className="py-7">
<h3 className="text-xl font-medium">Rooms and beds</h3>
<div className="mt-6 relative flex flex-col space-y-5">
<NcInputNumber label="Beds" max={10} />
<NcInputNumber label="Bedrooms" max={10} />
<NcInputNumber label="Bathrooms" max={10} />
</div>
</div> */}
{/* ---- */}
{/* <div className="py-7">
<h3 className="text-xl font-medium">Amenities</h3>
<div className="mt-6 relative ">
{renderMoreFilterItem(moreFilter1)}
</div>
</div> */}
{/* ---- */}
{/* <div className="py-7">
<h3 className="text-xl font-medium">Facilities</h3>
<div className="mt-6 relative ">
{renderMoreFilterItem(moreFilter2)}
</div>
</div> */}
{/* ---- */}
{/* <div className="py-7">
<h3 className="text-xl font-medium">Property type</h3>
<div className="mt-6 relative ">
{renderMoreFilterItem(moreFilter3)}
</div>
</div> */}
{/* ---- */}
{/* <div className="py-7">
<h3 className="text-xl font-medium">House rules</h3>
<div className="mt-6 relative ">
{renderMoreFilterItem(moreFilter4)}
</div>
</div> */}
</div>
</div>
<div className="p-4 sm:p-6 flex-shrink-0 bg-neutral-50 dark:bg-neutral-900 dark:border-t dark:border-neutral-800 flex items-center justify-between">
<ButtonThird
onClick={closeModalMoreFilterMobile}
sizeClass="px-4 py-2 sm:px-5"
>
Clear
</ButtonThird>
<ButtonPrimary
onClick={closeModalMoreFilterMobile}
sizeClass="px-4 py-2 sm:px-5"
>
Apply
</ButtonPrimary>
</div>
</div>
</Transition.Child>
</div>
</Dialog>
</Transition>
</div>
);
};
return (
<div className="flex lg:space-x-4">
<div className="hidden lg:flex space-x-4">
{renderTabsTypeOfPlace()}
{/* {renderTabsPriceRage()} */}
{/* {renderTabsRoomAndBeds()} */}
{/* {renderTabMoreFilter()} */}
</div>
{renderTabMoreFilterMobile()}
</div>
);
};
export default TabFilters;

14
src/app/tours/page.tsx

@ -0,0 +1,14 @@
import React, { FC } from "react";
import SectionGridFilterCard from "./SectionGridFilterCard";
export interface ListingStayMapPageProps {}
const ListingStayMapPage: FC<ListingStayMapPageProps> = ({}) => {
return (
<div className="container pb-24 lg:pb-28 2xl:pl-10 xl:pr-0 xl:max-w-none">
<SectionGridFilterCard />
</div>
);
};
export default ListingStayMapPage;

2
src/components/CardCategory3.tsx

@ -18,7 +18,7 @@ const CardCategory3: FC<CardCategory3Props> = ({
return (
<Link href={href} className={`nc-CardCategory3 flex flex-col ${className}`}>
<Link href={`/tours?country=${taxonomy.name}`} className={`nc-CardCategory3 flex flex-col ${className}`}>
<div
className={`flex-shrink-0 relative w-full aspect-w-5 aspect-h-5 sm:aspect-h-6 h-0 rounded-2xl overflow-hidden group`}
>

9
src/components/SectionGridFeaturePlaces.tsx

@ -9,6 +9,7 @@ import StayCard from "./StayCard";
import StayCard2 from "./StayCard2";
import axiosInstance from "./api/axios";
import { Context } from "./contexts/tourDetails";
import { useRouter } from "next/router";
// OTHER DEMO WILL PASS PROPS
const DEMO_DATA: StayDataType[] = DEMO_STAY_LISTINGS.filter((_, i) => i < 8);
@ -25,16 +26,18 @@ export interface SectionGridFeaturePlacesProps {
}
const SectionGridFeaturePlaces: FC<SectionGridFeaturePlacesProps> = ({
stayListings = DEMO_DATA,
gridClass = "",
heading = "List of Tours",
subHeading = "Popular places to stay that Chisfis recommends for you",
headingIsCenter,
cardType = "card2",
}) => {
const { countries, tours } = useContext(Context);
const [countryTours, setCountryTours] = useState();
useEffect(()=>{
handleChange("All")

20
src/shared/Checkbox.tsx

@ -1,5 +1,4 @@
"use client";
// Checkbox.tsx
import React, { FC } from "react";
export interface CheckboxProps {
@ -7,7 +6,7 @@ export interface CheckboxProps {
subLabel?: string;
className?: string;
name: string;
defaultChecked?: boolean;
checked?: boolean;
onChange?: (checked: boolean) => void;
}
@ -16,7 +15,7 @@ const Checkbox: FC<CheckboxProps> = ({
label = "",
name,
className = "",
defaultChecked,
checked,
onChange,
}) => {
return (
@ -25,18 +24,13 @@ const Checkbox: FC<CheckboxProps> = ({
id={name}
name={name}
type="checkbox"
className="focus:ring-action-primary h-6 w-6 text-primary-500 border-primary rounded border-neutral-500 bg-white dark:bg-neutral-700 dark:checked:bg-primary-500 focus:ring-primary-500"
defaultChecked={defaultChecked}
className="focus:ring-action-primary h-6 w-6 text-primary-500 border-primary rounded border-neutral-500 bg-white dark:bg-neutral-700 dark:checked:bg-primary-500 focus:ring-primary-500"
checked={checked}
onChange={(e) => onChange && onChange(e.target.checked)}
/>
{label && (
<label
htmlFor={name}
className="ml-3.5 flex flex-col flex-1 justify-center"
>
<span className=" text-neutral-900 dark:text-neutral-100">
{label}
</span>
<label htmlFor={name} className="ml-3.5 flex flex-col flex-1 justify-center">
<span className="text-neutral-900 dark:text-neutral-100">{label}</span>
{subLabel && (
<p className="mt-1 text-neutral-500 dark:text-neutral-400 text-sm font-light">
{subLabel}

85
yarn.lock

@ -7,7 +7,7 @@
resolved "https://registry.npmjs.org/@alloc/quick-lru/-/quick-lru-5.2.0.tgz"
integrity sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==
"@babel/runtime@^7.1.2", "@babel/runtime@^7.10.1", "@babel/runtime@^7.18.3", "@babel/runtime@^7.20.13", "@babel/runtime@^7.20.7", "@babel/runtime@^7.21.0":
"@babel/runtime@^7.1.2", "@babel/runtime@^7.10.1", "@babel/runtime@^7.18.3", "@babel/runtime@^7.20.7", "@babel/runtime@^7.21.0":
version "7.21.5"
resolved "https://registry.npmjs.org/@babel/runtime/-/runtime-7.21.5.tgz"
integrity sha512-8jI69toZqqcsnqGGqwGS4Qb1VwLOEp4hz+CXPywcvjs60u3B4Pom/U/7rm4W8tMOYEB+E9wgD0mW1l3r8qlI9Q==
@ -176,11 +176,6 @@
"@nodelib/fs.scandir" "2.1.5"
fastq "^1.6.0"
"@panva/hkdf@^1.0.2":
version "1.1.1"
resolved "https://registry.npmjs.org/@panva/hkdf/-/hkdf-1.1.1.tgz"
integrity sha512-dhPeilub1NuIG0X5Kvhh9lH4iW3ZsHlnzwgwbOlgwQ2wG1IqFzsgHqmKPk3WzsdWAeaxKJxgM0+W433RmN45GA==
"@pkgr/utils@^2.3.1":
version "2.4.1"
resolved "https://registry.npmjs.org/@pkgr/utils/-/utils-2.4.1.tgz"
@ -650,11 +645,6 @@ concat-map@0.0.1:
resolved "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz"
integrity sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==
cookie@^0.5.0:
version "0.5.0"
resolved "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz"
integrity sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==
copy-to-clipboard@^3.3.1:
version "3.3.3"
resolved "https://registry.npmjs.org/copy-to-clipboard/-/copy-to-clipboard-3.3.3.tgz"
@ -1778,11 +1768,6 @@ jiti@^1.18.2:
resolved "https://registry.npmjs.org/jiti/-/jiti-1.18.2.tgz"
integrity sha512-QAdOptna2NYiSSpv0O/BwoHBSmz4YhpzJHyi+fnMRTXFjp7B8i/YG5Z8IfusxB1ufjcD2Sre1F3R+nX3fvy7gg==
jose@^4.14.4, jose@^4.15.5:
version "4.15.9"
resolved "https://registry.npmjs.org/jose/-/jose-4.15.9.tgz"
integrity sha512-1vUQX+IdDMVPj4k8kOxgUqlcK518yluMuGZwqlr44FS1ppZB/5GWh4rZG89erpOBOJjU/OBsnCVFfapsRz6nEA==
js-cookie@^2.2.1:
version "2.2.1"
resolved "https://registry.npmjs.org/js-cookie/-/js-cookie-2.2.1.tgz"
@ -2001,22 +1986,7 @@ natural-compare@^1.4.0:
resolved "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz"
integrity sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==
next-auth@^4.24.7:
version "4.24.7"
resolved "https://registry.npmjs.org/next-auth/-/next-auth-4.24.7.tgz"
integrity sha512-iChjE8ov/1K/z98gdKbn2Jw+2vLgJtVV39X+rCP5SGnVQuco7QOr19FRNGMIrD8d3LYhHWV9j9sKLzq1aDWWQQ==
dependencies:
"@babel/runtime" "^7.20.13"
"@panva/hkdf" "^1.0.2"
cookie "^0.5.0"
jose "^4.15.5"
oauth "^0.9.15"
openid-client "^5.4.0"
preact "^10.6.3"
preact-render-to-string "^5.1.19"
uuid "^8.3.2"
"next@^12.2.5 || ^13 || ^14", next@^13.4.3:
next@^13.4.3:
version "13.4.3"
resolved "https://registry.npmjs.org/next/-/next-13.4.3.tgz"
integrity sha512-FV3pBrAAnAIfOclTvncw9dDohyeuEEXPe5KNcva91anT/rdycWbgtu3IjUj4n5yHnWK8YEPo0vrUecHmnmUNbA==
@ -2068,21 +2038,11 @@ npm-run-path@^5.1.0:
dependencies:
path-key "^4.0.0"
oauth@^0.9.15:
version "0.9.15"
resolved "https://registry.npmjs.org/oauth/-/oauth-0.9.15.tgz"
integrity sha512-a5ERWK1kh38ExDEfoO6qUHJb32rd7aYmPHuyCu3Fta/cnICvYmgd2uhuKXvPD+PXB+gCEYYEaQdIRAjCOwAKNA==
object-assign@^4.0.1, object-assign@^4.1.1:
version "4.1.1"
resolved "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz"
integrity sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==
object-hash@^2.2.0:
version "2.2.0"
resolved "https://registry.npmjs.org/object-hash/-/object-hash-2.2.0.tgz"
integrity sha512-gScRMn0bS5fH+IuwyIFgnh9zBdo4DV+6GhygmWM9HyNJSgS0hScp1f5vjtm7oIIOiT9trXrShAkLFSc2IqKNgw==
object-hash@^3.0.0:
version "3.0.0"
resolved "https://registry.npmjs.org/object-hash/-/object-hash-3.0.0.tgz"
@ -2151,11 +2111,6 @@ object.values@^1.1.6:
define-properties "^1.1.4"
es-abstract "^1.20.4"
oidc-token-hash@^5.0.3:
version "5.0.3"
resolved "https://registry.npmjs.org/oidc-token-hash/-/oidc-token-hash-5.0.3.tgz"
integrity sha512-IF4PcGgzAr6XXSff26Sk/+P4KZFJVuHAJZj3wgO3vX2bMdNVp/QXTP3P7CEm9V1IdG8lDLY3HhiqpsE/nOwpPw==
once@^1.3.0:
version "1.4.0"
resolved "https://registry.npmjs.org/once/-/once-1.4.0.tgz"
@ -2187,16 +2142,6 @@ open@^9.1.0:
is-inside-container "^1.0.0"
is-wsl "^2.2.0"
openid-client@^5.4.0:
version "5.5.0"
resolved "https://registry.npmjs.org/openid-client/-/openid-client-5.5.0.tgz"
integrity sha512-Y7Xl8BgsrkzWLHkVDYuroM67hi96xITyEDSkmWaGUiNX6CkcXC3XyQGdv5aWZ6dukVKBFVQCADi9gCavOmU14w==
dependencies:
jose "^4.14.4"
lru-cache "^6.0.0"
object-hash "^2.2.0"
oidc-token-hash "^5.0.3"
optionator@^0.9.1:
version "0.9.1"
resolved "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz"
@ -2350,28 +2295,11 @@ postcss@8.4.14:
picocolors "^1.0.0"
source-map-js "^1.0.2"
preact-render-to-string@^5.1.19:
version "5.2.6"
resolved "https://registry.npmjs.org/preact-render-to-string/-/preact-render-to-string-5.2.6.tgz"
integrity sha512-JyhErpYOvBV1hEPwIxc/fHWXPfnEGdRKxc8gFdAZ7XV4tlzyzG847XAyEZqoDnynP88akM4eaHcSOzNcLWFguw==
dependencies:
pretty-format "^3.8.0"
preact@^10.6.3, preact@>=10:
version "10.17.1"
resolved "https://registry.npmjs.org/preact/-/preact-10.17.1.tgz"
integrity sha512-X9BODrvQ4Ekwv9GURm9AKAGaomqXmip7NQTZgY7gcNmr7XE83adOMJvd3N42id1tMFU7ojiynRsYnY6/BRFxLA==
prelude-ls@^1.2.1:
version "1.2.1"
resolved "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz"
integrity sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==
pretty-format@^3.8.0:
version "3.8.0"
resolved "https://registry.npmjs.org/pretty-format/-/pretty-format-3.8.0.tgz"
integrity sha512-WuxUnVtlWL1OfZFQFuqvnvs6MiAGk9UNsBostyBOB0Is9wb5uRESevA6rnl/rkksXaGX3GzZhPup5d6Vp1nFew==
prop-types@^15.7.2, prop-types@^15.8.1:
version "15.8.1"
resolved "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz"
@ -2425,7 +2353,7 @@ react-datepicker@^4.11.0:
react-onclickoutside "^6.12.2"
react-popper "^2.3.0"
react-dom@*, "react-dom@^15.5.x || ^16.x || ^17.x || ^18.x", "react-dom@^16 || ^17 || ^18", "react-dom@^16.0.0 || ^17.0.0 || ^18.0.0", "react-dom@^16.8.0 || ^17.0.0 || ^18.0.0", "react-dom@^16.8.0 || ^17 || ^18", "react-dom@^16.9.0 || ^17 || ^18", "react-dom@^17.0.2 || ^18", react-dom@^18.0.0, react-dom@^18.2.0, react-dom@>=16.9.0:
react-dom@*, "react-dom@^15.5.x || ^16.x || ^17.x || ^18.x", "react-dom@^16 || ^17 || ^18", "react-dom@^16.0.0 || ^17.0.0 || ^18.0.0", "react-dom@^16.8.0 || ^17.0.0 || ^18.0.0", "react-dom@^16.8.0 || ^17 || ^18", "react-dom@^16.9.0 || ^17 || ^18", react-dom@^18.0.0, react-dom@^18.2.0, react-dom@>=16.9.0:
version "18.2.0"
resolved "https://registry.npmjs.org/react-dom/-/react-dom-18.2.0.tgz"
integrity sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g==
@ -2505,7 +2433,7 @@ react-use@^17.4.0:
ts-easing "^0.2.0"
tslib "^2.1.0"
react@*, "react@^15.5.x || ^16.x || ^17.x || ^18.x", "react@^16 || ^17 || ^18", "react@^16.0.0 || ^17.0.0 || ^18.0.0", "react@^16.8.0 || ^17.0.0 || ^18.0.0", "react@^16.8.0 || ^17 || ^18", "react@^16.8.0 || ^17.0.0 || ^18.0.0", "react@^16.8.0 || 17 || 18", "react@^16.8.3 || ^17 || ^18", "react@^16.9.0 || ^17 || ^18", "react@^17.0.2 || ^18", react@^18.0.0, react@^18.2.0, "react@>= 16", "react@>= 16.8.0 || 17.x.x || ^18.0.0-0", react@>=16.8, react@>=16.8.0, react@>=16.9.0:
react@*, "react@^15.5.x || ^16.x || ^17.x || ^18.x", "react@^16 || ^17 || ^18", "react@^16.0.0 || ^17.0.0 || ^18.0.0", "react@^16.8.0 || ^17.0.0 || ^18.0.0", "react@^16.8.0 || ^17 || ^18", "react@^16.8.0 || ^17.0.0 || ^18.0.0", "react@^16.8.0 || 17 || 18", "react@^16.8.3 || ^17 || ^18", "react@^16.9.0 || ^17 || ^18", react@^18.0.0, react@^18.2.0, "react@>= 16", "react@>= 16.8.0 || 17.x.x || ^18.0.0-0", react@>=16.8, react@>=16.8.0, react@>=16.9.0:
version "18.2.0"
resolved "https://registry.npmjs.org/react/-/react-18.2.0.tgz"
integrity sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==
@ -3046,11 +2974,6 @@ util-deprecate@^1.0.2:
resolved "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz"
integrity sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==
uuid@^8.3.2:
version "8.3.2"
resolved "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz"
integrity sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==
warning@^4.0.2:
version "4.0.3"
resolved "https://registry.npmjs.org/warning/-/warning-4.0.3.tgz"

Loading…
Cancel
Save