341 lines
10 KiB
JavaScript
341 lines
10 KiB
JavaScript
import React, { useEffect } from "react";
|
|
import { useInView } from "react-intersection-observer";
|
|
import { RockingChair, LocateFixed, School, WorkflowIcon, Lock } from "lucide-react";
|
|
import PersonIcon from "@mui/icons-material/Person";
|
|
import StarIcon from "@mui/icons-material/Star";
|
|
import VisibilityIcon from "@mui/icons-material/Visibility";
|
|
import PersonAddIcon from "@mui/icons-material/PersonAdd";
|
|
import FilterModal from "../../feature/FilterModal";
|
|
import bride1 from "../../assets/images/bride1.jpg";
|
|
import bride2 from "../../assets/images/bride2.jpg";
|
|
import bride3 from "../../assets/images/bride3.jpg";
|
|
import bride4 from "../../assets/images/bride4.jpg";
|
|
|
|
import groom1 from "../../assets/images/groom1.jpg";
|
|
import groom2 from "../../assets/images/groom2.jpg";
|
|
import groom3 from "../../assets/images/groom3.jpg";
|
|
import groom4 from "../../assets/images/groom4.jpg";
|
|
|
|
import horoscope from "../../assets/images/horoscopeicon.png";
|
|
import { useNavigate } from "react-router-dom";
|
|
import toast from "react-hot-toast";
|
|
import { useSelector, useDispatch } from "react-redux";
|
|
import { updateFilter } from "../../redux/filterSlice";
|
|
import { useProfiles } from "../../hooks/useProfiles";
|
|
import ProfileCardUI from "../common/ProfileCardUI";
|
|
import ProfileCardSkeleton from "../common/ProfileCardSkeleton";
|
|
|
|
// Main Component
|
|
export default function MatchesInterface() {
|
|
const [showSkeleton, setShowSkeleton] = React.useState(false);
|
|
const navigate = useNavigate();
|
|
const dispatch = useDispatch();
|
|
const filters = useSelector((state) => state.filters);
|
|
const filterType = filters.filter_type;
|
|
const selectedTab = filterType || "all_matches";
|
|
const isPaidMember = filters.isPaidMember;
|
|
|
|
const { ref, inView } = useInView({
|
|
threshold: 0,
|
|
rootMargin: "300px"
|
|
});
|
|
// Fetch real profiles data
|
|
const {
|
|
data: profilesData,
|
|
isLoading,
|
|
fetchNextPage,
|
|
hasNextPage,
|
|
isFetchingNextPage,
|
|
} = useProfiles(filters);
|
|
const profiles =
|
|
profilesData?.pages.flatMap((page) => page?.data|| []) || [];
|
|
|
|
// const { ref, inView } = useInView();
|
|
|
|
// useEffect(() => {
|
|
// if (inView && hasNextPage && !isFetchingNextPage) {
|
|
// fetchNextPage();
|
|
// }
|
|
// }, [inView, hasNextPage, isFetchingNextPage]);
|
|
|
|
|
|
useEffect(() => {
|
|
if (inView && hasNextPage && !isFetchingNextPage) {
|
|
|
|
setShowSkeleton(true); // show skeleton
|
|
|
|
const timer = setTimeout(() => {
|
|
fetchNextPage();
|
|
setShowSkeleton(false); // hide skeleton after API call
|
|
}, 120); // 0.5 seconds
|
|
|
|
return () => clearTimeout(timer);
|
|
}
|
|
}, [inView, hasNextPage, isFetchingNextPage, fetchNextPage]);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
console.log("Fetched profiles:", profiles);
|
|
|
|
console.log({
|
|
inView,
|
|
hasNextPage,
|
|
isFetchingNextPage,
|
|
});
|
|
const tabs = [
|
|
{
|
|
id: "all_matches",
|
|
icon: <PersonIcon className="w-6 h-6" />,
|
|
title: "Your Matches",
|
|
description: "View all the profiles that match your preferences",
|
|
category: "All Matches",
|
|
},
|
|
{
|
|
id: "shorlisted_by_you",
|
|
icon: <StarIcon className="w-6 h-6" />,
|
|
title: "Shortlisted by you",
|
|
description: "Matches you have shortlisted",
|
|
category: "Based on activity",
|
|
},
|
|
{
|
|
id: "viewed_you",
|
|
icon: <VisibilityIcon className="w-6 h-6" />,
|
|
title: "Viewed you",
|
|
description: "Matches who have viewed your profile",
|
|
category: "Based on activity",
|
|
},
|
|
{
|
|
id: "shorlisted_you",
|
|
icon: <PersonAddIcon className="w-6 h-6" />,
|
|
title: "Shortlisted you",
|
|
description: "Matches who have shortlisted your profile",
|
|
category: "Based on activity",
|
|
},
|
|
{
|
|
id: "viewed_by_you",
|
|
icon: <VisibilityIcon className="w-6 h-6" />,
|
|
title: "Viewed by you",
|
|
description: "Matches you have viewed",
|
|
category: "Based on activity",
|
|
},
|
|
{
|
|
id: "newly_joined",
|
|
icon: <RockingChair className="w-6 h-6" />,
|
|
title: "Newly Joined",
|
|
description: "Matches who Joined within the last 30 days",
|
|
category: "Based on activity",
|
|
},
|
|
{
|
|
id: "location_matches",
|
|
icon: <LocateFixed className="w-6 h-6" />,
|
|
title: "Location matches",
|
|
description: "Matches near your location",
|
|
category: "Based on activity",
|
|
},
|
|
{
|
|
id: "education_matches",
|
|
icon: <School className="w-6 h-6" />,
|
|
title: "Education matches",
|
|
description: "Matches near your education match",
|
|
category: "Based on activity",
|
|
},
|
|
{
|
|
id: "job_matches",
|
|
icon: <WorkflowIcon className="w-6 h-6" />,
|
|
title: "Job matches",
|
|
description: "Matches near your job",
|
|
category: "Based on activity",
|
|
},
|
|
];
|
|
|
|
let currentCategory = "";
|
|
|
|
return (
|
|
|
|
<>
|
|
|
|
{/* <div className="grid grid-cols-1 md:grid-cols-[300px_auto] md:px-4 gap-2 md:gap-10" > */}
|
|
<div className="flex flex-col md:flex-row my-6" >
|
|
|
|
|
|
{/* Left Sidebar - Fixed on desktop, scrollable on mobile */}
|
|
|
|
<div className="w-full md:w-80">
|
|
<div
|
|
className="relative rounded-[10px] border border-gray-200 bg-white my-6
|
|
shadow-lg h-[400px] md:h-[600px] overflow-y-auto md:sticky md:top-[150px]"
|
|
>
|
|
|
|
<div className="py-2 px-4 sticky top-0 bg-[#fff] ">
|
|
<h2 className="text-xl font-bold text-green-900 mb-4 mt-6 first:mt-0">
|
|
Filter Matches </h2>
|
|
|
|
</div>
|
|
|
|
<div className="py-6 px-4 h-full" >
|
|
{tabs.map((tab) => {
|
|
const showCategory = tab.category !== currentCategory;
|
|
if (showCategory) {
|
|
currentCategory = tab.category;
|
|
}
|
|
|
|
return (
|
|
<div key={tab.id}>
|
|
{showCategory && (
|
|
<h2 className="text-xl font-bold text-gray-900 mb-4 mt-6 first:mt-0">
|
|
{tab.category}
|
|
</h2>
|
|
)}
|
|
<div
|
|
onClick={() => {
|
|
dispatch(updateFilter({ filter_type: tab.id }));
|
|
}}
|
|
className={`p-4 rounded-lg mb-3 cursor-pointer transition-all ${
|
|
selectedTab === tab.id
|
|
? "bg-green-50 border-l-4 border-green-600"
|
|
: "hover:bg-gray-50"
|
|
}`}
|
|
>
|
|
<div className="flex items-start gap-3">
|
|
<div
|
|
className={`mt-1 ${
|
|
selectedTab === tab.id
|
|
? "text-green-600"
|
|
: "text-gray-600"
|
|
}`}
|
|
>
|
|
{tab.icon}
|
|
</div>
|
|
<div className="flex-1">
|
|
<div className="flex items-center justify-between">
|
|
<h3
|
|
className={`font-semibold text-base ${
|
|
selectedTab === tab.id
|
|
? "text-green-900"
|
|
: "text-gray-900"
|
|
}`}
|
|
>
|
|
{tab.title}
|
|
</h3>
|
|
<svg
|
|
className={`w-5 h-5 ${
|
|
selectedTab === tab.id
|
|
? "text-green-600"
|
|
: "text-gray-400"
|
|
}`}
|
|
fill="none"
|
|
stroke="currentColor"
|
|
viewBox="0 0 24 24"
|
|
>
|
|
<path
|
|
strokeLinecap="round"
|
|
strokeLinejoin="round"
|
|
strokeWidth={2}
|
|
d="M9 5l7 7-7 7"
|
|
/>
|
|
</svg>
|
|
</div>
|
|
<p className="text-sm text-gray-600 mt-1">
|
|
{tab.description}
|
|
</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
);
|
|
})}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
{/* Right Content Area - Scrollable */}
|
|
<div className=" px-2 py-8 w-full md:w-9/12 ">
|
|
<div className="w-[100%] max-w-[1200px] mx-auto">
|
|
<div className="flex justify-between gap-2 itemes-center mb-8">
|
|
<h1 className="text-[24px] font-bold text-gray-900 ">
|
|
{tabs.find((t) => t.id === selectedTab)?.title}
|
|
</h1>
|
|
<div className="flex gap-2 items-center">
|
|
<div className="relative cursor-pointer" onClick={() => {
|
|
if (isPaidMember) {
|
|
navigate("/horoscoper-generate");
|
|
} else {
|
|
toast.error("Star Matching is locked for free members");
|
|
}
|
|
}}>
|
|
<img
|
|
src={horoscope}
|
|
className={!isPaidMember ? "opacity-50 blur-[1px]" : ""}
|
|
/>
|
|
{!isPaidMember && (
|
|
<div className="absolute inset-0 flex items-center justify-center">
|
|
<Lock className="w-4 h-4 text-black" />
|
|
</div>
|
|
)}
|
|
</div>
|
|
<FilterModal />
|
|
</div>
|
|
</div>
|
|
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-2 xl:grid-cols-3 gap-2">
|
|
{isLoading && !isFetchingNextPage ? (
|
|
[...Array(6)].map((_, i) => <ProfileCardSkeleton key={i} />)
|
|
) : profiles.length > 0 ? (
|
|
profiles.map((profile) => (
|
|
<ProfileCardUI key={profile.id} profile={profile} />
|
|
))
|
|
) : !isLoading && !isFetchingNextPage ? (
|
|
<div className="col-span-full text-center py-10 text-gray-500">
|
|
No profiles found
|
|
</div>
|
|
) : null}
|
|
|
|
|
|
{/* {isFetchingNextPage &&
|
|
[...Array(5)].map((_, i) => (
|
|
<ProfileCardSkeleton key={`skel-${i}`} />
|
|
))} */}
|
|
|
|
|
|
{(isFetchingNextPage || showSkeleton) &&
|
|
[...Array(6)].map((_, i) => (
|
|
<ProfileCardSkeleton key={`skel-${i}`} />
|
|
))}
|
|
|
|
</div>
|
|
<div ref={ref} className="h-[20px]">
|
|
{!isLoading && !hasNextPage && profiles.length > 0 && (
|
|
<p className="text-center text-gray-500 py-8">
|
|
You've reached the end.
|
|
</p>
|
|
)}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
</>
|
|
|
|
|
|
|
|
|
|
);
|
|
}
|