Commit fd123e8b by jaymehta

filters final

1 parent 61d982d8
...@@ -15,13 +15,14 @@ const Detail = () => { ...@@ -15,13 +15,14 @@ const Detail = () => {
const dispatch = useDispatch(); const dispatch = useDispatch();
const { allActivitiesData } = useSelector(state => state.allActivitiesData); const { allActivitiesData } = useSelector(state => state.allActivitiesData);
const { activityById } = useSelector(state => state.activityById); const { activityById } = useSelector(state => state.activityById);
const { loadedUser } = useSelector(state => state.loadedUser);
useEffect(() => { useEffect(() => {
// console.log("router", activityById.data.attributes.category?.data?.attributes.name); // console.log("router", activityById.data.attributes.category?.data?.attributes.name);
dispatch(getActivitiesByFilters({ category: activityById.data.attributes.category?.data?.id })); dispatch(getActivitiesByFilters({ category: activityById?.data.attributes.category?.data?.id }));
}, [activityById]); }, [activityById]);
console.log("activityById", activityById); console.log("activityById", activityById);
// console.log("allActivitiesData", allActivitiesData); console.log("loadedUser", loadedUser);
return ( return (
<Fragment> <Fragment>
<main> <main>
...@@ -30,12 +31,12 @@ const Detail = () => { ...@@ -30,12 +31,12 @@ const Detail = () => {
<div className="row hide-on-mobile"> <div className="row hide-on-mobile">
<div className="col-12"> <div className="col-12">
<div className="breadcrum$eq: categoryb-container"> <div className="breadcrum$eq: categoryb-container">
<Breadcrumb> {/* <Breadcrumb>
<Breadcrumb.Item href="#">Home</Breadcrumb.Item> <Breadcrumb.Item href="#">Home</Breadcrumb.Item>
<Breadcrumb.Item href="#">New York</Breadcrumb.Item> <Breadcrumb.Item href="#">New York</Breadcrumb.Item>
<Breadcrumb.Item href="#">Adventure</Breadcrumb.Item> <Breadcrumb.Item href="#">Adventure</Breadcrumb.Item>
<Breadcrumb.Item active>Mountain Climbing</Breadcrumb.Item> <Breadcrumb.Item active>Mountain Climbing</Breadcrumb.Item>
</Breadcrumb> </Breadcrumb> */}
</div> </div>
</div> </div>
</div> </div>
......
...@@ -13,9 +13,13 @@ import { fadeIn, zoomIn, slideFromLeft, slideFromRight } from "../animationvaria ...@@ -13,9 +13,13 @@ import { fadeIn, zoomIn, slideFromLeft, slideFromRight } from "../animationvaria
import { motion } from "framer-motion"; import { motion } from "framer-motion";
import { cleanImage } from "../../services/imageHandling.js"; import { cleanImage } from "../../services/imageHandling.js";
import { useRouter } from "next/router.js"; import { useRouter } from "next/router.js";
import WishlistComponent from "./WIshlistComponent.js";
import { useSelector } from "react-redux";
const SimilarExperiences = ({ allActivitiesData }) => { const SimilarExperiences = ({ allActivitiesData }) => {
const router = useRouter(); const router = useRouter();
const [listingData, setListingData] = useState([]); const [listingData, setListingData] = useState([]);
const { endUser } = useSelector(state => state.endUser);
let ListingData = []; let ListingData = [];
useEffect(() => { useEffect(() => {
console.log("allActivitiesData", allActivitiesData); console.log("allActivitiesData", allActivitiesData);
...@@ -37,58 +41,6 @@ const SimilarExperiences = ({ allActivitiesData }) => { ...@@ -37,58 +41,6 @@ const SimilarExperiences = ({ allActivitiesData }) => {
setListingData(ListingData); setListingData(ListingData);
}, [allActivitiesData]); }, [allActivitiesData]);
// ListingData = [
// {
// image: "/images/Browse-Experiences/01.png",
// topRated: "Top Rated",
// title: "City Climb",
// discription: "Lorem ipsum dolor sit amet, consectetur adipiscing elit,",
// price: "200",
// offPrice: "40%",
// days: "For 1 Night",
// taxes: "Includes taxes & Fees"
// },
// {
// image: "/images/Browse-Experiences/02.png",
// topRated: "Top Rated",
// title: "City Climb",
// discription: "Lorem ipsum dolor sit amet, consectetur adipiscing elit,",
// price: "200",
// offPrice: "40%",
// days: "For 1 Night",
// taxes: "Includes taxes & Fees"
// },
// {
// image: "/images/Browse-Experiences/03.png",
// topRated: "Top Rated",
// title: "City Climb",
// discription: "Lorem ipsum dolor sit amet, consectetur adipiscing elit,",
// price: "200",
// offPrice: "40%",
// days: "For 1 Night",
// taxes: "Includes taxes & Fees"
// },
// {
// image: "/images/Browse-Experiences/04.png",
// topRated: "Top Rated",
// title: "City Climb",
// discription: "Lorem ipsum dolor sit amet, consectetur adipiscing elit,",
// price: "200",
// offPrice: "40%",
// days: "For 1 Night",
// taxes: "Includes taxes & Fees"
// },
// {
// image: "/images/Browse-Experiences/01.png",
// topRated: "Top Rated",
// title: "City Climb",
// discription: "Lorem ipsum dolor sit amet, consectetur adipiscing elit,",
// price: "200",
// offPrice: "40%",
// days: "For 1 Night",
// taxes: "Includes taxes & Fees"
// }
// ];
const projects = { const projects = {
responsive: { responsive: {
0: { 0: {
...@@ -130,11 +82,6 @@ const SimilarExperiences = ({ allActivitiesData }) => { ...@@ -130,11 +82,6 @@ const SimilarExperiences = ({ allActivitiesData }) => {
<div className="browse-experiences-carousal position-relative"> <div className="browse-experiences-carousal position-relative">
<Swiper <Swiper
slidesPerView={1} slidesPerView={1}
// autoplay={{
// delay: 2500,
// disableOnInteraction: false
// }}
// spaceBetween={10}
navigation={{ nextEl: ".similar-experiences-arrow-left", prevEl: ".similar-experiences-arrow-right" }} navigation={{ nextEl: ".similar-experiences-arrow-left", prevEl: ".similar-experiences-arrow-right" }}
breakpoints={{ breakpoints={{
640: { 640: {
...@@ -175,11 +122,12 @@ const SimilarExperiences = ({ allActivitiesData }) => { ...@@ -175,11 +122,12 @@ const SimilarExperiences = ({ allActivitiesData }) => {
<Image layout="fill" alt="" className="image img-fluid" src="/images/icons/star.svg" /> <Image layout="fill" alt="" className="image img-fluid" src="/images/icons/star.svg" />
</span> </span>
</div> </div>
<div className="wishlist"> {endUser && <WishlistComponent userId={endUser.id} activityId={data.id} />}
{/* <div className="wishlist">
<span className="image-container"> <span className="image-container">
<Image layout="fill" alt="" className="image img-fluid" src="/images/icons/wishlist.svg" /> <Image layout="fill" alt="" className="image img-fluid" src="/images/icons/wishlist.svg" />
</span> </span>
</div> </div> */}
</div> </div>
</div> </div>
<div className="discription"> <div className="discription">
......
import Image from "next/image";
import React, { useEffect, useState } from "react";
import { useDispatch, useSelector } from "react-redux";
import { checkWishlist, deleteWishlist, getWishlists, toggleWishlist } from "../../redux/actions/activityAction";
const WishlistComponent = ({ activityId, userId }) => {
const { wishlists } = useSelector(state => state.wishlists);
const [isActive, setisActive] = useState(false);
const [wishlistId, setwishlistId] = useState();
const dispatch = useDispatch();
// console.log("wishlists", wishlists);
useEffect(() => {
wishlists &&
wishlists.length > 0 &&
wishlists.map(item => {
if (item.attributes.experience.data.id == activityId) {
setisActive(true);
setwishlistId(item.id);
}
});
}, [wishlists]);
return (
<div>
{activityId && userId && (
<div className="wishlist">
<span
onClick={async e => {
e.preventDefault();
if (isActive) {
setisActive(false);
const response = await deleteWishlist({ wishlistId });
dispatch(getWishlists({ endUser: userId }));
// console.log("delete", response);
} else {
setisActive(true);
const response = await toggleWishlist({ activityId, userId });
dispatch(getWishlists({ endUser: userId }));
}
// console.log("response wishlist ", response);
}}
className="image-container"
>
<Image layout="fill" alt="" className="image img-fluid" src={isActive ? "/images/icons/wishlist-01-active.svg" : "/images/icons/wishlist-01.svg"} />
</span>
</div>
)}
</div>
);
};
export default WishlistComponent;
...@@ -5,11 +5,14 @@ import React, { useEffect, useState } from "react"; ...@@ -5,11 +5,14 @@ import React, { useEffect, useState } from "react";
import { useDispatch, useSelector } from "react-redux"; import { useDispatch, useSelector } from "react-redux";
import { cleanImage } from "../../services/imageHandling"; import { cleanImage } from "../../services/imageHandling";
import { Button, Container, Form, Nav, Navbar } from "react-bootstrap"; import { Button, Container, Form, Nav, Navbar } from "react-bootstrap";
import { loadUser } from "../../redux/actions/userActions"; import { getCurrentEndUser, loadUser } from "../../redux/actions/userActions";
import { useRouter } from "next/router"; import { useRouter } from "next/router";
import { getWishlists } from "../../redux/actions/activityAction";
const Header = () => { const Header = () => {
const { loadedUser } = useSelector(state => state.loadedUser); const { loadedUser } = useSelector(state => state.loadedUser);
const { endUser } = useSelector(state => state.endUser);
const dispatch = useDispatch(); const dispatch = useDispatch();
// console.log("user", loadedUser); // console.log("user", loadedUser);
const [isSticky, setIsSticky] = useState(false); const [isSticky, setIsSticky] = useState(false);
...@@ -29,6 +32,13 @@ const Header = () => { ...@@ -29,6 +32,13 @@ const Header = () => {
window.removeEventListener("scroll", handleScroll); window.removeEventListener("scroll", handleScroll);
}; };
}, []); }, []);
useEffect(() => {
dispatch(getCurrentEndUser());
}, []);
useEffect(() => {
if (endUser) dispatch(getWishlists({ endUser: endUser.id }));
}, [endUser]);
return ( return (
<header className={`header_wrap ${isSticky ? "stick" : ""}`}> <header className={`header_wrap ${isSticky ? "stick" : ""}`}>
<Navbar expand="lg" className="bg-body-tertiary"> <Navbar expand="lg" className="bg-body-tertiary">
...@@ -69,8 +79,8 @@ const Header = () => { ...@@ -69,8 +79,8 @@ const Header = () => {
<Button <Button
onClick={async () => { onClick={async () => {
signOut({ redirect: false }); signOut({ redirect: false });
await router.push("/") await router.push("/");
window.location.reload() window.location.reload();
}} }}
className="me-3" className="me-3"
variant="primary" variant="primary"
...@@ -80,14 +90,22 @@ const Header = () => { ...@@ -80,14 +90,22 @@ const Header = () => {
</div> </div>
) : ( ) : (
<div> <div>
<Button onClick={()=> { <Button
router.push("/signup/user") onClick={() => {
}} className="me-3" variant="primary"> router.push("/signup/user");
}}
className="me-3"
variant="primary"
>
Sign Up Sign Up
</Button> </Button>
<Button onClick={()=> { <Button
router.push("/login/user") onClick={() => {
}} className="" variant="primary"> router.push("/login/user");
}}
className=""
variant="primary"
>
Log In Log In
</Button> </Button>
</div> </div>
......
import Image from "next/image"; import Image from "next/image";
import React, { useState } from "react"; import React, { useEffect, useState } from "react";
import { fadeIn, zoomIn, slideFromLeft, slideFromRight } from "../animationvariants.js"; import { fadeIn, zoomIn, slideFromLeft, slideFromRight } from "../animationvariants.js";
import { Button } from "react-bootstrap"; import { Button } from "react-bootstrap";
...@@ -11,59 +11,38 @@ import { motion } from "framer-motion"; ...@@ -11,59 +11,38 @@ import { motion } from "framer-motion";
import "swiper/css"; import "swiper/css";
import "swiper/css/pagination"; import "swiper/css/pagination";
import "swiper/css/navigation"; import "swiper/css/navigation";
const LetDiscover = () => { import { cleanImage } from "../../services/imageHandling.js";
import { useRouter } from "next/router.js";
import { Loader } from "react-bootstrap-typeahead";
import { useDispatch, useSelector } from "react-redux";
import { getSubCategoriesByCategoryId } from "../../redux/actions/categoriesAction.js";
import { setActivityFilters } from "../../redux/actions/activityAction.js";
import SubCategoryChips from "./SubCategoryChips.js";
const LetDiscover = ({ categories }) => {
const { subCategories, loading } = useSelector(sate => sate.subCategories);
const { activityFilters } = useSelector(sate => sate.activityFilters);
const router = useRouter();
const dispatch = useDispatch();
const [activeIndex, setActiveIndex] = useState(null); const [activeIndex, setActiveIndex] = useState(null);
const handleTitleClick = index => { // const [loading, setloading] = useState(null);
console.log("categories", categories);
console.log("subCategories", subCategories);
console.log("activityFilters", activityFilters);
const handleTitleClick = ({ index, data }) => {
// setloading(true);
// Check if the clicked index is already active // Check if the clicked index is already active
if (activeIndex === index) { if (activeIndex === index) {
return; // Do nothing if it's already active return; // Do nothing if it's already active
} }
// Toggle the active index if it's different from the clicked index // Toggle the active index if it's different from the clicked index
setActiveIndex(index); setActiveIndex(index);
// setloading(false);
}; };
const letDiscoverData = [ // useEffect(() => {
{ // dispatch(getSubCategoriesByCategoryId(router.query.category));
image: "/images/discover/01.png", // }, [router]);
title: "All"
},
{
image: "/images/discover/02.png",
title: "Art & History"
},
{
image: "/images/discover/03.png",
title: "Experiential Dining "
},
{
image: "/images/discover/04.png",
title: "Family"
},
{
image: "/images/discover/05.png",
title: "Fitness"
},
{
image: "/images/discover/06.png",
title: "Performing Arts"
},
{
image: "/images/discover/01.png",
title: "Pop Ups"
},
{
image: "/images/discover/02.png",
title: "Tour"
},
{
image: "/images/discover/03.png",
title: "Classes"
},
{
image: "/images/discover/04.png",
title: "Wellness"
}
];
const [showCloseIcon, setShowCloseIcon] = useState({}); const [showCloseIcon, setShowCloseIcon] = useState({});
const handleToggleCloseIcon = index => { const handleToggleCloseIcon = index => {
setShowCloseIcon(prevShowCloseIcon => ({ setShowCloseIcon(prevShowCloseIcon => ({
...@@ -71,32 +50,7 @@ const LetDiscover = () => { ...@@ -71,32 +50,7 @@ const LetDiscover = () => {
[index]: !prevShowCloseIcon[index] [index]: !prevShowCloseIcon[index]
})); }));
}; };
const subCategoriesData = [ console.log("showCloseIcon", showCloseIcon);
{
name: "Boat"
},
{
name: "Games"
},
{
name: "Helicopter"
},
{
name: "Ice-skating"
},
{
name: "Jet-skiing"
},
{
name: "Kayaking"
},
{
name: "Park"
},
{
name: "Rooftop"
}
];
return ( return (
<> <>
<section className="let-discover-session let-discover-listing-session"> <section className="let-discover-session let-discover-listing-session">
...@@ -144,20 +98,46 @@ const LetDiscover = () => { ...@@ -144,20 +98,46 @@ const LetDiscover = () => {
modules={[Navigation, Autoplay]} modules={[Navigation, Autoplay]}
className="mySwiper01 mySwiper02" className="mySwiper01 mySwiper02"
> >
{letDiscoverData && <SwiperSlide>
letDiscoverData.map((data, index) => { <motion.div variants={zoomIn("left", 0.3)} initial={"hidden"} whileInView={"show"} viewport={{ once: false, amount: 0.2 }}>
<a
className={activeIndex=="All" ? "active" : ""}
onClick={() => {
dispatch(setActivityFilters({ filters: {} }));
handleTitleClick({ index: "All" });
}}
>
<span className="image-container">
<Image layout="fill" alt="" className="image img-fluid" src={"/images/discover/01.png"} />
</span>
<div className="title">{"All"}</div>
</a>
</motion.div>
</SwiperSlide>
{categories &&
categories.data.map((data, index) => {
const isActive = activeIndex === index; const isActive = activeIndex === index;
return ( return (
<SwiperSlide> <>
<motion.div variants={zoomIn("left", 0.3)} initial={"hidden"} whileInView={"show"} viewport={{ once: false, amount: 0.2 }}> <SwiperSlide>
<a className={isActive ? "active" : ""} onClick={() => handleTitleClick(index)}> <motion.div variants={zoomIn("left", 0.3)} initial={"hidden"} whileInView={"show"} viewport={{ once: false, amount: 0.2 }}>
<span className="image-container"> <a
<Image layout="fill" alt="" className="image img-fluid" src={data.image} /> className={isActive ? "active" : ""}
</span> onClick={async () => {
<div className="title">{data.title}</div> // router.push(`?category=${data.id}`);
</a> dispatch(setActivityFilters({ filters: { ...activityFilters, category: data.id, subCategories: [] } }));
</motion.div> dispatch(getSubCategoriesByCategoryId(data.id));
</SwiperSlide> handleTitleClick({ index, data });
}}
>
<span className="image-container">
<Image layout="fill" alt="" className="image img-fluid" src={cleanImage(data.attributes.image.data.attributes)} />
</span>
<div className="title">{data.attributes.name}</div>
</a>
</motion.div>
</SwiperSlide>
</>
); );
})} })}
</Swiper> </Swiper>
...@@ -179,20 +159,25 @@ const LetDiscover = () => { ...@@ -179,20 +159,25 @@ const LetDiscover = () => {
</div> </div>
</div> </div>
<div className="row"> <div className="row">
<div className="col-12"> <div className="col-12 text-center">
<div className="sub-categories"> {!loading ? (
{subCategoriesData && <div className="sub-categories">
subCategoriesData.map((data, index) => ( {activityFilters.category && subCategories &&
<a key={index} onClick={() => handleToggleCloseIcon(index)}> subCategories.data.map((data, index) => (
<span>{data.name}</span> <SubCategoryChips key={index} data={data} />
{showCloseIcon[index] && ( // <a key={index} onClick={() => handleToggleCloseIcon(index)}>
<span className="image-container"> // <span>{data.attributes.name}</span>
<Image layout="fill" alt="image" className="image" src="/images/icons/close-button.svg" /> // {showCloseIcon[index] && (
</span> // <span className="image-container">
)} // <Image layout="fill" alt="image" className="image" src="/images/icons/close-button.svg" />
</a> // </span>
))} // )}
</div> // </a>
))}
</div>
) : (
<Loader />
)}
</div> </div>
</div> </div>
</div> </div>
......
import React, { Fragment } from "react"; import React, { Fragment, useEffect } from "react";
import ListingInner from "./ListingInner"; import ListingInner from "./ListingInner";
import SignUpToExperienceOurPlatform from "../home/SignUpToExperienceOurPlatform"; import SignUpToExperienceOurPlatform from "../home/SignUpToExperienceOurPlatform";
import LetDiscover from "./LetDiscover"; import LetDiscover from "./LetDiscover";
...@@ -6,12 +6,16 @@ import { useSelector } from "react-redux"; ...@@ -6,12 +6,16 @@ import { useSelector } from "react-redux";
const Listing = () => { const Listing = () => {
const { allActivitiesData } = useSelector(sate => sate.allActivitiesData); const { allActivitiesData } = useSelector(sate => sate.allActivitiesData);
console.log("allActivitiesData >>>> 123", allActivitiesData) const { categories } = useSelector(sate => sate.categories);
const { subCategories } = useSelector(sate => sate.subCategories);
console.log("allActivitiesData >>>> 123", allActivitiesData);
return ( return (
<Fragment> <Fragment>
<main> <main>
<LetDiscover /> <LetDiscover categories={categories} subCategories={subCategories}/>
<ListingInner allActivitiesData={allActivitiesData} /> <ListingInner allActivitiesData={allActivitiesData} />
<SignUpToExperienceOurPlatform /> <SignUpToExperienceOurPlatform />
</main> </main>
......
...@@ -2,12 +2,17 @@ import React, { useEffect, useState } from "react"; ...@@ -2,12 +2,17 @@ import React, { useEffect, useState } from "react";
import ListingFilter from "./ListingFilter"; import ListingFilter from "./ListingFilter";
import ListingItems from "./ListingItems"; import ListingItems from "./ListingItems";
import Image from "next/image"; import Image from "next/image";
import { getActivitiesByFilters, getWishlists, setActivityFilters } from "../../redux/actions/activityAction";
import { useDispatch, useSelector } from "react-redux";
const ListingInner = ({allActivitiesData}) => { const ListingInner = ({ allActivitiesData }) => {
console.log("allActivitiesData >>>>>", allActivitiesData) console.log("allActivitiesData", allActivitiesData);
const [isFilterViewOpen, setIsFilterViewOpen] = useState(false); const [isFilterViewOpen, setIsFilterViewOpen] = useState(false);
const [isGridViewOpen, setIsGridViewOpen] = useState(false); const [isGridViewOpen, setIsGridViewOpen] = useState(false);
const [isOpen, setIsOpen] = useState(false); const [isOpen, setIsOpen] = useState(false);
const { activityFilters } = useSelector(sate => sate.activityFilters);
const dispatch = useDispatch();
const [size, setSize] = useState(768); const [size, setSize] = useState(768);
const toggleGridViewDropdown = () => { const toggleGridViewDropdown = () => {
...@@ -36,14 +41,70 @@ const ListingInner = ({allActivitiesData}) => { ...@@ -36,14 +41,70 @@ const ListingInner = ({allActivitiesData}) => {
window.removeEventListener("resize", handleResize); window.removeEventListener("resize", handleResize);
}; };
}, []); }, []);
// USEEFFECT FOR FILTERS
useEffect(() => {
let filters = {};
// console.log("subcategory here", activityFilters.startDate);
if (activityFilters.category) {
filters["category"] = { id: { $eq: activityFilters.category } };
}
if (activityFilters.subCategories?.length > 0) {
filters["category"] = null;
filters["subCategory"] = activityFilters.subCategories.map(item => {
return { subCategory: { id: { $eq: item } } };
});
}
if (activityFilters.activityType) {
filters["activityType"] = activityFilters.activityType;
}
if (activityFilters.startDate) {
filters["fromDate"] = activityFilters.startDate;
}
if (activityFilters.endDate) {
filters["toDate"] = activityFilters.endDate;
}
if (activityFilters.minDuration > 0) {
filters["minimumDuration"] = activityFilters.minDuration;
}
if (activityFilters.maxDuration > 0) {
filters["maximumDuration"] = activityFilters.maxDuration;
}
if (activityFilters.minGroupSize > 0) {
filters["minGroupSize"] = activityFilters.minGroupSize;
}
if (activityFilters.maxGroupSize > 0) {
filters["maxGroupSize"] = activityFilters.maxGroupSize;
}
if (activityFilters.minAge > 0) {
filters["ageLowerLimit"] = activityFilters.minAge;
}
if (activityFilters.sorting) {
filters["sort"] = activityFilters.sorting;
}
// console.log("subcategory >>", filters);
dispatch(
getActivitiesByFilters({
subCategory: filters.subCategory,
category: filters.category,
activityType: filters.activityType,
fromDate: filters.fromDate,
toDate: filters.toDate,
minimumDuration: filters.minimumDuration,
maximumDuration: filters.maximumDuration,
minGroupSize: filters.minGroupSize,
maxGroupSize: filters.maxGroupSize,
ageLowerLimit: filters.ageLowerLimit,
sort: filters.sort
})
);
}, [activityFilters]);
return ( return (
<> <>
<section className="listing-inner-session"> <section className="listing-inner-session">
<div className="container-fluid"> <div className="container-fluid">
<div className="row"> <div className="row">
<div className="col-12"> <div className="col-12">
<div className="filter-dd"> <div className="filter-dd">
...@@ -53,26 +114,6 @@ const ListingInner = ({allActivitiesData}) => { ...@@ -53,26 +114,6 @@ const ListingInner = ({allActivitiesData}) => {
<Image layout="fill" alt="" className="image img-fluid" src="/images/icons/filter-view.svg" /> <Image layout="fill" alt="" className="image img-fluid" src="/images/icons/filter-view.svg" />
</span> </span>
</a> </a>
{/* <div className={`inner-content ${isGridViewOpen ? "open" : ""}`}>
<div className="top-head">
<div className="">View By</div>
<div className="close-btn" onClick={toggleGridViewDropdown}>
<span className="image-container">
<Image layout="fill" alt="" className="image img-fluid" src="/images/icons/close-icon.svg" />
</span>
</div>
</div>
<ul className="list-by">
<li>
<input className="form-check-labe" type="radio" id="3 Grid View" name="gridView" />
<label htmlFor="3 Grid View">3 Grid View</label>
</li>
<li>
<input className="form-check-labe" type="radio" id="4 Grid View" name="gridView" />
<label htmlFor="4 Grid View">4 Grid View</label>
</li>
</ul>
</div> */}
</div> </div>
<div className="grid-view box-inner"> <div className="grid-view box-inner">
<a onClick={toggleGridViewDropdown}> <a onClick={toggleGridViewDropdown}>
...@@ -118,21 +159,41 @@ const ListingInner = ({allActivitiesData}) => { ...@@ -118,21 +159,41 @@ const ListingInner = ({allActivitiesData}) => {
</div> </div>
<ul className="list-by"> <ul className="list-by">
<li> <li>
<input className="form-check-labe" type="radio" id="Price - High to Low" name="sort" /> <input
className="form-check-labe"
onChange={e => {
dispatch(setActivityFilters({ filters: { ...activityFilters, sorting: e.target.value } }));
// console.log(e.target.value);
}}
type="radio"
id="Price - High to Low"
name="sort"
value={"descending"}
/>
<label htmlFor="Price - High to Low">Price - High to Low</label> <label htmlFor="Price - High to Low">Price - High to Low</label>
</li> </li>
<li> <li>
<input className="form-check-labe" type="radio" id="Price - Low to High" name="sort" /> <input
className="form-check-labe"
onChange={e => {
dispatch(setActivityFilters({ filters: { ...activityFilters, sorting: e.target.value } }));
// console.log(e.target.value);
}}
type="radio"
id="Price - Low to High"
name="sort"
value={"ascending"}
/>
<label htmlFor="Price - Low to High">Price - Low to High</label> <label htmlFor="Price - Low to High">Price - Low to High</label>
</li> </li>
<li> {/* <li>
<input className="form-check-labe" type="radio" id="Most Rated" name="sort" /> <input className="form-check-labe" type="radio" id="Most Rated" name="sort" />
<label htmlFor="Most Rated">Most Rated</label> <label htmlFor="Most Rated">Most Rated</label>
</li> </li>
<li> <li>
<input className="form-check-labe" type="radio" id="Most Popular" name="sort" /> <input className="form-check-labe" type="radio" id="Most Popular" name="sort" />
<label htmlFor="Most Popular">Most Popular</label> <label htmlFor="Most Popular">Most Popular</label>
</li> </li> */}
</ul> </ul>
</div> </div>
</div> </div>
......
...@@ -2,10 +2,13 @@ import Image from "next/image"; ...@@ -2,10 +2,13 @@ import Image from "next/image";
import { useRouter } from "next/router"; import { useRouter } from "next/router";
import React from "react"; import React from "react";
import { Button } from "react-bootstrap"; import { Button } from "react-bootstrap";
import { useSelector } from "react-redux";
import { cleanImage } from "../../services/imageHandling"; import { cleanImage } from "../../services/imageHandling";
import WishlistComponent from "../detail/WIshlistComponent";
const ListingItems = ({ allActivitiesData }) => { const ListingItems = ({ allActivitiesData }) => {
console.log("allActivitiesData", allActivitiesData); const { endUser } = useSelector(state => state.endUser);
console.log("endUser", endUser);
const router = useRouter(); const router = useRouter();
return ( return (
<> <>
...@@ -33,11 +36,7 @@ const ListingItems = ({ allActivitiesData }) => { ...@@ -33,11 +36,7 @@ const ListingItems = ({ allActivitiesData }) => {
<Image layout="fill" alt="" className="image img-fluid" src="/images/icons/star.svg" /> <Image layout="fill" alt="" className="image img-fluid" src="/images/icons/star.svg" />
</span> </span>
</div> </div>
<div className="wishlist"> {endUser && <WishlistComponent activityId={data.id} userId={endUser.id} />}
<span className="image-container">
<Image layout="fill" alt="" className="image img-fluid" src="/images/icons/wishlist.svg" />
</span>
</div>
</div> </div>
</div> </div>
<div className="discription"> <div className="discription">
......
import Image from "next/image";
import React, { useEffect, useState } from "react";
import { useDispatch, useSelector } from "react-redux";
import { setActivityFilters } from "../../redux/actions/activityAction";
const SubCategoryChips = ({ data }) => {
const dispatch = useDispatch();
const { activityFilters } = useSelector(sate => sate.activityFilters);
const [select, setselect] = useState(activityFilters.subCategories?.includes(data.id));
useEffect(() => {
setselect(activityFilters.subCategories?.includes(data.id));
}, [activityFilters]);
return (
<div>
<a
onClick={() => {
setselect(!select);
if (activityFilters.subCategories.length > 0) {
if (activityFilters.subCategories.includes(data.id)) {
let newArr = activityFilters.subCategories.filter(item => item != data.id);
dispatch(setActivityFilters({ filters: { ...activityFilters, subCategories: newArr } }));
} else {
dispatch(setActivityFilters({ filters: { ...activityFilters, subCategories: [...activityFilters.subCategories, data.id] } }));
}
} else {
dispatch(setActivityFilters({ filters: { ...activityFilters, subCategories: [data.id] } }));
}
}}
// key={index} onClick={() => handleToggleCloseIcon(index)}
>
<span>{data.attributes.name}</span>
{select && (
<span className="image-container">
<Image layout="fill" alt="image" className="image" src="/images/icons/close-button.svg" />
</span>
)}
</a>
</div>
);
};
export default SubCategoryChips;
...@@ -865,7 +865,7 @@ const ActivityDetails = ({ isUpdate }) => { ...@@ -865,7 +865,7 @@ const ActivityDetails = ({ isUpdate }) => {
</div> </div>
<div className="col-2 col-lg-4"> <div className="col-2 col-lg-4">
<label>Minimum duration</label> <label>Maximum duration</label>
<input <input
value={values.maxDuration} value={values.maxDuration}
id="maxDuration" id="maxDuration"
......
import React from "react"; import React, { useEffect } from "react";
import { useDispatch } from "react-redux";
import Detail from "../../components/detail/Detail"; import Detail from "../../components/detail/Detail";
import Layout from "../../components/layout/Layout"; import Layout from "../../components/layout/Layout";
import { getActivitiesByFilters, getActivityById } from "../../redux/actions/activityAction"; import { getActivitiesByFilters, getActivityById } from "../../redux/actions/activityAction";
import { loadUser } from "../../redux/actions/userActions";
import { wrapper } from "../../redux/store"; import { wrapper } from "../../redux/store";
const ActivityDetailPage = () => { const ActivityDetailPage = () => {
const dispatch = useDispatch();
useEffect(() => {
dispatch(loadUser());
}, []);
return ( return (
<Layout> <Layout>
<Detail /> <Detail />
...@@ -16,9 +23,8 @@ export default ActivityDetailPage; ...@@ -16,9 +23,8 @@ export default ActivityDetailPage;
/** For server side rendering */ /** For server side rendering */
export const getServerSideProps = wrapper.getServerSideProps(store => async ({ req, query }) => { export const getServerSideProps = wrapper.getServerSideProps(store => async ({ req, query }) => {
await store.dispatch(getActivityById(query.id));
await store.dispatch(getActivityById(query.id)) // await store.dispatch(getActivitiesByFilters({category: query.category}))
// await store.dispatch(getActivitiesByFilters({category: query.category}))
return { return {
props: {} props: {}
......
import Layout from "../components/layout/Layout"; import Layout from "../components/layout/Layout";
import Listing from "../components/listing/Listing"; import Listing from "../components/listing/Listing";
import { getActivitiesByFilters } from "../redux/actions/activityAction"; import { getActivitiesByFilters } from "../redux/actions/activityAction";
import { getAllCategories, getAllSubCategories, getSubCategoriesByCategoryId } from "../redux/actions/categoriesAction";
import { wrapper } from "../redux/store"; import { wrapper } from "../redux/store";
export default function ListingPage() { export default function ListingPage() {
...@@ -23,19 +24,21 @@ export default function ListingPage() { ...@@ -23,19 +24,21 @@ export default function ListingPage() {
/** For server side rendering */ /** For server side rendering */
export const getServerSideProps = wrapper.getServerSideProps(store => async ({ req, query }) => { export const getServerSideProps = wrapper.getServerSideProps(store => async ({ req, query }) => {
try { try {
await store.dispatch(getActivitiesByFilters({})) await store.dispatch(getActivitiesByFilters({}));
await store.dispatch(getAllCategories());
await store.dispatch(getSubCategoriesByCategoryId());
return { return {
props: {}, props: {}
// Next.js will attempt to re-generate the page: // Next.js will attempt to re-generate the page:
// - Any requests to the page after the initial request and before 10 seconds are also cached and instantaneous. // - Any requests to the page after the initial request and before 10 seconds are also cached and instantaneous.
// - After the 10-second window, the next request will still show the cached (stale) page // - After the 10-second window, the next request will still show the cached (stale) page
// - Next.js triggers a regeneration of the page in the background. // - Next.js triggers a regeneration of the page in the background.
// - Once the page generates successfully, Next.js will invalidate the cache and show the updated page. If the background regeneration fails, the old page would still be unaltered. // - Once the page generates successfully, Next.js will invalidate the cache and show the updated page. If the background regeneration fails, the old page would still be unaltered.
// In seconds // In seconds
// revalidate: Number(process.env.NEXT_PUBLIC_ISR_REVALIDATE_AFTER) // revalidate: Number(process.env.NEXT_PUBLIC_ISR_REVALIDATE_AFTER)
}; };
} catch (error) { } catch (error) {
console.log("index.js", error); console.log("index.js", error);
} }
}); });
import axios from "axios"; import axios from "axios";
import { getSession } from "next-auth/react"; import { getSession } from "next-auth/react";
import { import {
ACTIVITY_FILTERS_CONSTANT,
CREATE_ACTIVITY_FAIL, CREATE_ACTIVITY_FAIL,
CREATE_ACTIVITY_REQUEST, CREATE_ACTIVITY_REQUEST,
CREATE_ACTIVITY_SUCCESS, CREATE_ACTIVITY_SUCCESS,
...@@ -10,6 +11,9 @@ import { ...@@ -10,6 +11,9 @@ import {
GET_ACTIVITY_BY_ID_FAIL, GET_ACTIVITY_BY_ID_FAIL,
GET_ACTIVITY_BY_ID_REQUEST, GET_ACTIVITY_BY_ID_REQUEST,
GET_ACTIVITY_BY_ID_SUCCESS, GET_ACTIVITY_BY_ID_SUCCESS,
GET_WISHLISTS_FAIL,
GET_WISHLISTS_REQUEST,
GET_WISHLISTS_SUCCESS,
UPDATE_ACTIVITY_BY_ID_FAIL, UPDATE_ACTIVITY_BY_ID_FAIL,
UPDATE_ACTIVITY_BY_ID_REQUEST, UPDATE_ACTIVITY_BY_ID_REQUEST,
UPDATE_ACTIVITY_BY_ID_SUCCESS UPDATE_ACTIVITY_BY_ID_SUCCESS
...@@ -315,8 +319,9 @@ export const getActivitiesForEndUser = () => async dispatch => { ...@@ -315,8 +319,9 @@ export const getActivitiesForEndUser = () => async dispatch => {
}; };
export const getActivitiesByFilters = export const getActivitiesByFilters =
({ category, subCategory, price, ageLimit, duration, fromDate, toDate, minGroupSize, maxGroupSize, minDuration, maxDuration, activityType }) => ({ category, subCategory, price, ageLowerLimit, duration, fromDate, toDate, minGroupSize, maxGroupSize, minimumDuration, maximumDuration, activityType, sort }) =>
async dispatch => { async dispatch => {
let sortFilter = [];
try { try {
dispatch({ dispatch({
type: GET_ACTIVITIES_REQUEST, type: GET_ACTIVITIES_REQUEST,
...@@ -328,24 +333,72 @@ export const getActivitiesByFilters = ...@@ -328,24 +333,72 @@ export const getActivitiesByFilters =
"Content-Type": "application/json" "Content-Type": "application/json"
} }
}; };
if (sort == "descending") {
sortFilter = ["pricePerPerson:desc"];
} else {
sortFilter = ["pricePerPerson:asc"];
}
let query = { let query = {
filters: { filters: {
approved: { $eq: true } approved: { $eq: true }
// fromDate: { $gte: fromDate },
// toDate: { $lte: toDate },
// $and: [{ fromDate: { $gte: "2024-05-05" } }, { toDate: { $lte: toDate } }]
// $or: subCategory
}, },
populate: ["masterMonths", "subCategory", "subCategory.category", "timeSlots", "masterPincode", "vendor", "image", "category"] populate: ["masterMonths", "subCategory", "subCategory.category", "timeSlots", "masterPincode", "vendor", "image", "category"]
// sort: ["pricePerPerson:asc"]
}; };
console.log("query:", category);
if (category) { if (category) {
query.filters["category"] = { id: { $eq: category } }; query.filters["category"] = category;
}
if (subCategory) {
query.filters["$or"] = subCategory;
}
if (activityType) {
query.filters["activityType"] = activityType;
} }
if (fromDate && toDate) {
query.filters["$and"] = [{ fromDate: { $gte: fromDate } }, { toDate: { $lte: toDate } }];
delete query.filters.fromDate;
delete query.filters.toDate;
}
if (!(fromDate && toDate)) {
if (fromDate) {
query.filters["fromDate"] = { $gte: fromDate };
}
if (toDate) {
query.filters["toDate"] = { $lte: toDate };
}
}
if (maximumDuration) {
query.filters["maximumDuration"] = { $lte: maximumDuration };
}
if (minimumDuration) {
query.filters["minimumDuration"] = { $gte: minimumDuration };
}
if (minGroupSize) {
query.filters["minGroupSize"] = { $gte: minGroupSize };
}
if (maxGroupSize) {
query.filters["maxGroupSize"] = { $lte: maxGroupSize };
}
if (ageLowerLimit) {
query.filters["ageLowerLimit"] = { $gte: ageLowerLimit };
}
if (sort) {
query["sort"] = sortFilter;
}
// console.log("subcategoryquery:", query);
const queryString = qs.stringify(query, { const queryString = qs.stringify(query, {
encodeValuesOnly: true encodeValuesOnly: true
}); });
const response = await axios.get(`${process.env.NEXT_PUBLIC_BACKEND_API_URL}/api/experiences?${queryString}`, config); const response = await axios.get(`${process.env.NEXT_PUBLIC_BACKEND_API_URL}/api/experiences?${queryString}`, config);
// console.log("activity res", response);
dispatch({ dispatch({
type: GET_ACTIVITIES_SUCCESS, type: GET_ACTIVITIES_SUCCESS,
payload: response.data payload: response.data
...@@ -359,3 +412,135 @@ export const getActivitiesByFilters = ...@@ -359,3 +412,135 @@ export const getActivitiesByFilters =
}); });
} }
}; };
export const setActivityFilters =
({ filters }) =>
async dispatch => {
dispatch({
type: ACTIVITY_FILTERS_CONSTANT,
payload: filters
});
};
export const getWishlists =
({ endUser }) =>
async dispatch => {
try {
const session = await getSession();
if (!session) {
return;
}
dispatch({
type: GET_WISHLISTS_REQUEST
});
const config = {
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${session.jwt}`
}
};
const query = {
filters: {
endUser: {
id: {
$eq: endUser
}
}
},
populate: ["endUser", "experience"]
};
const queryString = qs.stringify(query, {
encodeValuesOnly: true
});
const response = await axios.get(`${process.env.NEXT_PUBLIC_BACKEND_API_URL}/api/wishlists?${queryString}`, config);
dispatch({
type: GET_WISHLISTS_SUCCESS,
payload: response.data.data
});
return response.data.data;
} catch (error) {
dispatch({
type: GET_WISHLISTS_FAIL,
payload: error.response.data
});
}
};
export const toggleWishlist = async ({ activityId, userId }) => {
try {
const session = await getSession();
if (!session) {
return;
}
const config = {
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${session.jwt}`
}
};
const data = {
experience: activityId,
endUser: userId
};
// console.log("data", data);
const response = await axios.post(`${process.env.NEXT_PUBLIC_BACKEND_API_URL}/api/wishlists`, { data }, config);
// console.log("response.data.data", response.data);
return response.data.data;
} catch (error) {}
};
export const checkWishlist = async ({ activityId, userId }) => {
try {
const session = await getSession();
if (!session) {
return;
}
const config = {
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${session.jwt}`
}
};
const data = {
experience: activityId,
endUser: userId
};
const query = {
filters: {
$and: [{ experience: { id: { $eq: activityId } } }, { endUser: { id: { $eq: userId } } }]
},
populate: ["experience", "endUser"]
};
const queryString = qs.stringify(query, {
encodeValuesOnly: true
});
// console.log("data", data);
const response = await axios.get(`${process.env.NEXT_PUBLIC_BACKEND_API_URL}/api/wishlists?${queryString}`, config);
console.log("response.data.data", response.data);
return response.data.data;
} catch (error) {}
};
export const deleteWishlist = async ({ wishlistId }) => {
try {
const session = await getSession();
if (!session) {
return;
}
const config = {
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${session.jwt}`
}
};
const res = await axios.delete(`${process.env.NEXT_PUBLIC_BACKEND_API_URL}/api/wishlists/${wishlistId}`, config);
return res.data;
} catch (error) {}
};
...@@ -91,3 +91,52 @@ export const getAllSubCategories = categoryName => async dispatch => { ...@@ -91,3 +91,52 @@ export const getAllSubCategories = categoryName => async dispatch => {
}); });
} }
}; };
export const getSubCategoriesByCategoryId = categoryId => async dispatch => {
try {
dispatch({
type: GET_SUB_CATEGORIES_REQUEST,
loading: true
});
const config = {
headers: {
"Content-Type": "application/json"
}
};
let query = {
filters: {
category: {
id: {}
}
},
populate: ["category"]
};
if (categoryId) {
// console.log("here 1", query.filters.category);
query.filters.category.id = { $eq: categoryId };
}
// console.log(">>>", query);
const queryString = qs.stringify(query, {
encodeValuesOnly: true
});
const response = await axios.get(`${process.env.NEXT_PUBLIC_BACKEND_API_URL}/api/sub-categories/?${queryString}`, config);
dispatch({
type: GET_SUB_CATEGORIES_SUCCESS,
payload: response.data,
loading: false
});
return response.data;
} catch (error) {
dispatch({
type: GET_SUB_CATEGORIES_FAIL,
payload: error.response.data
});
}
};
...@@ -14,8 +14,10 @@ export const UPDATE_ACTIVITY_BY_ID_REQUEST = "UPDATE_ACTIVITY_BY_ID_REQUEST" ...@@ -14,8 +14,10 @@ export const UPDATE_ACTIVITY_BY_ID_REQUEST = "UPDATE_ACTIVITY_BY_ID_REQUEST"
export const UPDATE_ACTIVITY_BY_ID_SUCCESS = "UPDATE_ACTIVITY_BY_ID_SUCCESS" export const UPDATE_ACTIVITY_BY_ID_SUCCESS = "UPDATE_ACTIVITY_BY_ID_SUCCESS"
export const UPDATE_ACTIVITY_BY_ID_FAIL = "UPDATE_ACTIVITY_BY_ID_FAIL" export const UPDATE_ACTIVITY_BY_ID_FAIL = "UPDATE_ACTIVITY_BY_ID_FAIL"
// export const GET_ACTIVITY_BY_ID_REQUEST = "GET_ACTIVITY_BY_ID_REQUEST" export const GET_WISHLISTS_REQUEST = "GET_WISHLISTS_REQUEST"
// export const GET_ACTIVITY_BY_ID_SUCCESS = "GET_ACTIVITY_BY_ID_SUCCESS" export const GET_WISHLISTS_SUCCESS = "GET_WISHLISTS_SUCCESS"
// export const GET_ACTIVITY_BY_ID_FAIL = "GET_ACTIVITY_BY_ID_FAIL" export const GET_WISHLISTS_FAIL = "GET_WISHLISTS_FAIL"
export const ACTIVITY_FILTERS_CONSTANT = "ACTIVITY_FILTERS_CONSTANT"
export const CLEAR_ERRORS = "CLEAR_ERRORS"; export const CLEAR_ERRORS = "CLEAR_ERRORS";
\ No newline at end of file \ No newline at end of file
import { import {
ACTIVITY_FILTERS_CONSTANT,
CREATE_ACTIVITY_FAIL, CREATE_ACTIVITY_FAIL,
CREATE_ACTIVITY_REQUEST, CREATE_ACTIVITY_REQUEST,
CREATE_ACTIVITY_SUCCESS, CREATE_ACTIVITY_SUCCESS,
...@@ -8,6 +9,9 @@ import { ...@@ -8,6 +9,9 @@ import {
GET_ACTIVITY_BY_ID_FAIL, GET_ACTIVITY_BY_ID_FAIL,
GET_ACTIVITY_BY_ID_REQUEST, GET_ACTIVITY_BY_ID_REQUEST,
GET_ACTIVITY_BY_ID_SUCCESS, GET_ACTIVITY_BY_ID_SUCCESS,
GET_WISHLISTS_FAIL,
GET_WISHLISTS_REQUEST,
GET_WISHLISTS_SUCCESS,
UPDATE_ACTIVITY_BY_ID_FAIL, UPDATE_ACTIVITY_BY_ID_FAIL,
UPDATE_ACTIVITY_BY_ID_REQUEST, UPDATE_ACTIVITY_BY_ID_REQUEST,
UPDATE_ACTIVITY_BY_ID_SUCCESS UPDATE_ACTIVITY_BY_ID_SUCCESS
...@@ -123,3 +127,43 @@ export const updateActivityByIdReducer = (state = {}, action) => { ...@@ -123,3 +127,43 @@ export const updateActivityByIdReducer = (state = {}, action) => {
return state; return state;
} }
}; };
export const setActivityFilterReducer = (state = { activityFilters: {} }, action) => {
switch (action.type) {
case ACTIVITY_FILTERS_CONSTANT:
return {
activityFilters: action.payload
};
default:
return state;
}
};
export const getWishlistsReducer = (state = {}, action) => {
switch (action.type) {
case GET_WISHLISTS_REQUEST:
return { loading: true };
case GET_WISHLISTS_SUCCESS:
return {
loading: false,
wishlists: action.payload
};
case GET_WISHLISTS_FAIL:
return {
loading: false,
error: action.payload.error.message
};
case CLEAR_ERRORS:
return {
...state,
error: null
};
default:
return state;
}
};
...@@ -5,7 +5,7 @@ import { authReducer, forgotPasswordReducer, getEndUserReducer, loadedUserReduce ...@@ -5,7 +5,7 @@ import { authReducer, forgotPasswordReducer, getEndUserReducer, loadedUserReduce
import { enquiryReducer, getEnquiriesReducer } from "./enquiryReducer"; import { enquiryReducer, getEnquiriesReducer } from "./enquiryReducer";
import { displayEnquireNowReducer } from "./enquireNowModalReducer"; import { displayEnquireNowReducer } from "./enquireNowModalReducer";
import { getAllVendorsReducer, getVendorDetailsReducer, loggedInVendorReducer, updateVendorReducer } from "./vendorReducers"; import { getAllVendorsReducer, getVendorDetailsReducer, loggedInVendorReducer, updateVendorReducer } from "./vendorReducers";
import { createActivityReducer, getActivitiesReducer, getActivityByIdReducer, updateActivityByIdReducer } from "./activitiesReducer"; import { createActivityReducer, getActivitiesReducer, getActivityByIdReducer, getWishlistsReducer, setActivityFilterReducer, updateActivityByIdReducer } from "./activitiesReducer";
import { getAllCategoriesReducer, getAllSubCategoriesReducer } from "./categoryReducer"; import { getAllCategoriesReducer, getAllSubCategoriesReducer } from "./categoryReducer";
import { getAllTestimonialReducer } from "./testimonialReducer"; import { getAllTestimonialReducer } from "./testimonialReducer";
import { blogReducer, blogsReducer } from "./blogReducer"; import { blogReducer, blogsReducer } from "./blogReducer";
...@@ -40,6 +40,8 @@ const reducers = combineReducers({ ...@@ -40,6 +40,8 @@ const reducers = combineReducers({
allVendors: getAllVendorsReducer, allVendors: getAllVendorsReducer,
endUser: getEndUserReducer, endUser: getEndUserReducer,
enquiriesByVendor: getEnquiriesReducer, enquiriesByVendor: getEnquiriesReducer,
activityFilters: setActivityFilterReducer,
wishlists: getWishlistsReducer,
}); });
export default reducers; export default reducers;
Styling with Markdown is supported
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!