370 lines
13 KiB
JavaScript
370 lines
13 KiB
JavaScript
import { useState, useEffect,useRef, useMemo } from 'react';
|
|
import { Heart, X, RotateCcw, MapPin, Briefcase, GraduationCap } from 'lucide-react';
|
|
import LazyImage from './LazyImage';
|
|
import nomoreimg from "../../assets/images/nomoreimg.png";
|
|
import { useNavigate } from "react-router-dom";
|
|
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 Tooltip from '@mui/material/Tooltip';
|
|
const TinderCard = ({ children, onSwipe, onCardLeftScreen, preventSwipe, className }) => {
|
|
const [pos, setPos] = useState({ x: 0, y: 0 });
|
|
const [dragging, setDragging] = useState(false);
|
|
const [startPos, setStartPos] = useState({ x: 0, y: 0 });
|
|
const [gone, setGone] = useState(false);
|
|
|
|
useEffect(() => {
|
|
// Reset state on mount
|
|
setPos({ x: 0, y: 0 });
|
|
setDragging(false);
|
|
setGone(false);
|
|
}, []);
|
|
|
|
const handleStart = (clientX, clientY) => {
|
|
if (gone) return;
|
|
setDragging(true);
|
|
setStartPos({ x: clientX - pos.x, y: clientY - pos.y });
|
|
};
|
|
|
|
const handleMove = (clientX, clientY) => {
|
|
if (!dragging || gone) return;
|
|
setPos({ x: clientX - startPos.x, y: clientY - startPos.y });
|
|
};
|
|
|
|
const handleEnd = () => {
|
|
if (gone) return;
|
|
setDragging(false);
|
|
if (Math.abs(pos.x) > 120) {
|
|
const dir = pos.x > 0 ? 'right' : 'left';
|
|
if (onSwipe) onSwipe(dir);
|
|
setGone(true);
|
|
setTimeout(() => onCardLeftScreen && onCardLeftScreen(dir), 300);
|
|
} else {
|
|
setPos({ x: 0, y: 0 });
|
|
}
|
|
};
|
|
|
|
if (gone) return null;
|
|
|
|
const rotation = pos.x / 20;
|
|
const opacity = Math.min(Math.abs(pos.x) / 100, 1);
|
|
|
|
return (
|
|
<div
|
|
className={className}
|
|
style={{
|
|
transform: `translate(${pos.x}px, ${pos.y}px) rotate(${rotation}deg)`,
|
|
transition: dragging ? 'none' : 'transform 0.3s ease-out',
|
|
touchAction: 'none'
|
|
}}
|
|
onMouseDown={(e) => handleStart(e.clientX, e.clientY)}
|
|
onMouseMove={(e) => handleMove(e.clientX, e.clientY)}
|
|
onMouseUp={handleEnd}
|
|
onMouseLeave={() => dragging && handleEnd()}
|
|
onTouchStart={(e) => handleStart(e.touches[0].clientX, e.touches[0].clientY)}
|
|
onTouchMove={(e) => handleMove(e.touches[0].clientX, e.touches[0].clientY)}
|
|
onTouchEnd={handleEnd}
|
|
>
|
|
{pos.x > 50 && (
|
|
<div className="absolute top-8 left-8 z-10 border-4 border-green-500 text-green-500 px-4 py-2 rounded-lg text-2xl font-bold rotate-[-20deg]" style={{ opacity }}>
|
|
LIKE
|
|
</div>
|
|
)}
|
|
{pos.x < -50 && (
|
|
<div className="absolute top-8 right-8 z-10 border-4 border-red-500 text-red-500 px-4 py-2 rounded-lg text-2xl font-bold rotate-[20deg]" style={{ opacity }}>
|
|
NOPE
|
|
</div>
|
|
)}
|
|
{children}
|
|
</div>
|
|
);
|
|
};
|
|
|
|
const profiles = [
|
|
{
|
|
id: 1,
|
|
name: 'Priya Sharma',
|
|
age: 26,
|
|
location: 'Mumbai, India',
|
|
profession: 'Software Engineer',
|
|
education: 'B.Tech from IIT Bombay',
|
|
about: 'Love traveling, reading, and cooking. Looking for someone who values family.',
|
|
image: bride1
|
|
},
|
|
{
|
|
id: 2,
|
|
name: 'Ananya Patel',
|
|
age: 24,
|
|
location: 'Bangalore, India',
|
|
profession: 'Doctor',
|
|
education: 'MBBS from AIIMS',
|
|
about: 'Passionate about healthcare and music. Seeking a caring life partner.',
|
|
image: bride2
|
|
},
|
|
{
|
|
id: 3,
|
|
name: 'Neha Gupta',
|
|
age: 27,
|
|
location: 'Delhi, India',
|
|
profession: 'CA',
|
|
education: 'CA from ICAI',
|
|
about: 'Enjoy yoga and meditation. Looking for someone with similar values.',
|
|
image: bride3
|
|
},
|
|
{
|
|
id: 4,
|
|
name: 'Kavya Reddy',
|
|
age: 25,
|
|
location: 'Hyderabad, India',
|
|
profession: 'Marketing Manager',
|
|
education: 'MBA from ISB',
|
|
about: 'Creative soul who loves art and dance. Seeking a supportive partner.',
|
|
image: bride4
|
|
}
|
|
];
|
|
|
|
export default function MatrimonySwipeCards() {
|
|
const [swipedCards, setSwipedCards] = useState(new Set());
|
|
const [lastDirection, setLastDirection] = useState('');
|
|
const [likedProfiles, setLikedProfiles] = useState([]);
|
|
const [dislikedProfiles, setDislikedProfiles] = useState([]);
|
|
const [key, setKey] = useState(0);
|
|
const navigate = useNavigate();
|
|
|
|
const activeProfiles = profiles.filter(p => !swipedCards.has(p.id));
|
|
const canSwipe = activeProfiles.length > 0;
|
|
|
|
|
|
// **CHECK TOKEN FOR BLUR CONTROL**
|
|
const hasValidToken = useMemo(() => {
|
|
return !!localStorage.getItem("token");
|
|
}, []);
|
|
|
|
|
|
|
|
// **FIX: Ensure consistent order - Priya Sharma first**
|
|
const displayProfiles = useMemo(() => {
|
|
return activeProfiles.sort((a, b) => b.id - a.id ); // Sort by ID ascending (1,2,3,4)
|
|
}, [activeProfiles]);
|
|
|
|
const swiped = (direction, profile) => {
|
|
setLastDirection(direction);
|
|
setSwipedCards(prev => new Set([...prev, profile.id]));
|
|
if (direction === 'right') {
|
|
setLikedProfiles(prev => [...prev, profile]);
|
|
} else {
|
|
setDislikedProfiles(prev => [...prev, profile]);
|
|
}
|
|
};
|
|
|
|
// const swipe = (dir) => {
|
|
// if (canSwipe) {
|
|
// const topProfile = activeProfiles[activeProfiles.length - 1];
|
|
// swiped(dir, topProfile);
|
|
// }
|
|
// };
|
|
|
|
const swipe = (dir) => {
|
|
const token = localStorage.getItem("token");
|
|
|
|
if (!token) {
|
|
navigate("/login");
|
|
return;
|
|
}
|
|
|
|
navigate("/match");
|
|
|
|
if (canSwipe) {
|
|
// const topProfile = activeProfiles[activeProfiles.length - 1];
|
|
const topProfile = displayProfiles[0]; // Priya Sharma first
|
|
swiped(dir, topProfile);
|
|
}
|
|
};
|
|
|
|
|
|
const reset = () => {
|
|
setSwipedCards(new Set());
|
|
setLikedProfiles([]);
|
|
setDislikedProfiles([]);
|
|
setLastDirection('');
|
|
setKey(prev => prev + 1);
|
|
};
|
|
|
|
return (
|
|
<div className=" w-[100%] max-w-[fit-screen] p-2 md:px-6 overflow-hidden">
|
|
<div className="max-w-md mx-auto">
|
|
<h1 className="text-2xl font-bold text-center text-gray-800 mb-2">
|
|
Find Your Match
|
|
</h1>
|
|
<p className="text-center text-gray-600 mb-4 text-sm">
|
|
Swipe right to like, left to pass
|
|
</p>
|
|
|
|
{/* Card Container with overflow hidden */}
|
|
<div className="relative h-[480px] w-full overflow-hidden rounded-2xl">
|
|
<div className="absolute inset-0 flex items-center justify-center">
|
|
{!canSwipe ? (
|
|
<div className="text-center p-8 bg-white rounded-2xl shadow-lg">
|
|
<p className="text-xl text-gray-600 mb-4">No more profiles!</p>
|
|
<LazyImage
|
|
src={nomoreimg}
|
|
className="w-full h-[190px] object-cover "
|
|
|
|
/>
|
|
<button
|
|
onClick={reset}
|
|
className="cursor-pointer flex items-center gap-2 mx-auto px-6 py-3 bg-[#034E08] text-white rounded-full hover:bg-[#A70710] transition"
|
|
>
|
|
<RotateCcw size={20} />
|
|
Start Over
|
|
</button>
|
|
</div>
|
|
) : (
|
|
displayProfiles
|
|
.slice(0, 4) // Limit to 3 cards for stack effect
|
|
|
|
.map((profile, index) => {
|
|
|
|
const isTopCard = index === 3; // ALWAYS FIXED TOP CARD
|
|
// **NO BLUR IF TOKEN EXISTS**
|
|
const shouldBlur = !hasValidToken && !isTopCard;
|
|
|
|
return (
|
|
<TinderCard
|
|
key={`${key}-${profile.id}`}
|
|
onSwipe={(dir) => swiped(dir, profile)}
|
|
onCardLeftScreen={() => {}}
|
|
preventSwipe={['up', 'down']}
|
|
className="absolute w-full max-w-sm cursor-grab active:cursor-grabbing"
|
|
>
|
|
<div onClick={(e) => e.stopPropagation()} className="bg-white rounded-2xl shadow-xl overflow-hidden select-none">
|
|
<div className="relative">
|
|
<div classname=" relative bg-gray-200 overflow-hidden w-full max-w-sm h-[300px]" style={{height:"300px"}}>
|
|
|
|
<img
|
|
src={profile.image}
|
|
alt={profile.name}
|
|
className={`w-full h-full object-cover
|
|
${shouldBlur ? "blur-[4px] brightness-75 p-2" : ""}
|
|
`}
|
|
draggable={false}
|
|
|
|
/>
|
|
|
|
{/* LOCK ICON FOR NON TOP CARDS */}
|
|
{shouldBlur && (
|
|
<div className="absolute inset-0 flex items-center justify-center">
|
|
<svg width="60" height="60" fill="white">
|
|
<path d="M12 22v-6a10 10 0 1120 0v6h2a2 2 0 012 2v18a2 2 0 01-2 2H10a2 2 0 01-2-2V24a2 2 0 012-2h2zm4 0h16v-6a8 8 0 10-16 0v6z"/>
|
|
</svg>
|
|
</div>
|
|
)}
|
|
</div>
|
|
{/* White Gradient Overlay at bottom of image */}
|
|
<div
|
|
className="z-1 absolute bottom-0 left-0 right-0 h-15 pointer-events-none"
|
|
style={{
|
|
background:
|
|
"linear-gradient(rgba(255, 255, 255, 0) 0%, rgba(255, 255, 255, 0.75) 50%, rgb(255, 255, 255) 100%)",
|
|
}}
|
|
></div>
|
|
|
|
<div className="z-2 absolute bottom-1 left-0 right-0 pb-0 p-4">
|
|
<h2 className=" text-[22px] font-bold text-black">
|
|
{profile.name}, {profile.age}
|
|
</h2>
|
|
</div>
|
|
</div>
|
|
|
|
<div className="p-4 space-y-3">
|
|
<div className="flex items-center gap-2 text-gray-600">
|
|
<MapPin size={16} className="text-pink-500 flex-shrink-0" />
|
|
<span className="text-sm">{profile.location}</span>
|
|
</div>
|
|
<div className="flex items-center gap-2 text-gray-600">
|
|
<Briefcase size={16} className="text-purple-500 flex-shrink-0" />
|
|
<span className="text-sm">{profile.profession}</span>
|
|
</div>
|
|
<div className="flex items-center gap-2 text-gray-600">
|
|
<GraduationCap size={16} className="text-indigo-500 flex-shrink-0" />
|
|
<span className="text-sm">{profile.education}</span>
|
|
</div>
|
|
<p className="text-gray-700 text-sm mt-2 line-clamp-2">
|
|
{profile.about}
|
|
</p>
|
|
</div>
|
|
</div>
|
|
</TinderCard>
|
|
)
|
|
})
|
|
)}
|
|
</div>
|
|
</div>
|
|
|
|
{/* Action Buttons */}
|
|
{canSwipe && (
|
|
<div className="flex justify-center gap-6 mt-4">
|
|
{/* Decline Button with MUI Tooltip */}
|
|
<Tooltip title="Decline" placement="top" arrow>
|
|
<button
|
|
onClick={() => swipe('left')}
|
|
className="w-12 h-12 bg-white rounded-full shadow-lg flex items-center justify-center hover:bg-red-50 transition border-2 border-red-200 hover:border-red-400 active:scale-95"
|
|
>
|
|
<X size={26} className="text-red-500" />
|
|
</button>
|
|
</Tooltip>
|
|
|
|
{/* Reset Button with MUI Tooltip */}
|
|
<Tooltip title="Reset" placement="top" arrow>
|
|
<button
|
|
onClick={reset}
|
|
className="w-12 h-12 bg-white rounded-full shadow-lg flex items-center justify-center hover:bg-yellow-50 transition border-2 border-yellow-200 hover:border-yellow-400 active:scale-95 self-center"
|
|
>
|
|
<RotateCcw size={20} className="text-yellow-500" />
|
|
</button>
|
|
</Tooltip>
|
|
|
|
{/* Interest Button with MUI Tooltip */}
|
|
<Tooltip title="Show Interest" placement="top" arrow>
|
|
<button
|
|
onClick={() => swipe('right')}
|
|
className="w-12 h-12 bg-white rounded-full shadow-lg flex items-center justify-center hover:bg-green-50 transition border-2 border-green-200 hover:border-green-400 active:scale-95"
|
|
>
|
|
<Heart size={26} className="text-green-500" />
|
|
</button>
|
|
</Tooltip>
|
|
</div>
|
|
)}
|
|
|
|
|
|
{/* Stats */}
|
|
{/* <div className="mt-6 flex gap-4 text-sm">
|
|
<div className="flex-1 bg-green-100 rounded-lg p-3 text-center">
|
|
<p className="font-semibold text-green-700">👍 Liked: {likedProfiles.length}</p>
|
|
</div>
|
|
<div className="flex-1 bg-red-100 rounded-lg p-3 text-center">
|
|
<p className="font-semibold text-red-700">👎 Passed: {dislikedProfiles.length}</p>
|
|
</div>
|
|
</div> */}
|
|
|
|
{/* Liked Profiles Preview */}
|
|
{/* {likedProfiles.length > 0 && (
|
|
<div className="mt-4 bg-white rounded-xl p-4 shadow-md">
|
|
<h3 className="font-semibold text-gray-700 mb-3">💚 Your Likes</h3>
|
|
<div className="flex gap-2 flex-wrap">
|
|
{likedProfiles.map(p => (
|
|
<div key={p.id} className="flex items-center gap-2 bg-green-50 px-3 py-1.5 rounded-full">
|
|
<img src={p.image} alt={p.name} className="w-6 h-6 rounded-full object-cover" />
|
|
<span className="text-sm text-green-700">{p.name.split(' ')[0]}</span>
|
|
</div>
|
|
))}
|
|
</div>
|
|
</div>
|
|
)} */}
|
|
</div>
|
|
</div>
|
|
);
|
|
} |