Spaces:
Sleeping
Sleeping
| import { useState, useEffect } from 'react'; | |
| import api from '../api/api'; | |
| import { Star, MapPin, Sparkles, TrendingUp, Loader2 } from 'lucide-react'; | |
| import { Link } from 'react-router-dom'; | |
| const Recommendations = () => { | |
| const [recs, setRecs] = useState([]); | |
| const [loading, setLoading] = useState(true); | |
| useEffect(() => { | |
| const fetchRecs = async () => { | |
| try { | |
| // Fetch recommendations (mocking some user preferences or using defaults) | |
| const response = await api.get('/recommendations?specialty=portrait'); | |
| setRecs(response.data); | |
| setLoading(false); | |
| } catch (error) { | |
| console.error('Failed to fetch recommendations:', error); | |
| setLoading(false); | |
| } | |
| }; | |
| fetchRecs(); | |
| }, []); | |
| if (loading) return <div className="flex justify-center py-10"><Loader2 className="animate-spin text-blue-600" /></div>; | |
| if (recs.length === 0) return null; | |
| return ( | |
| <section className="py-10"> | |
| <div className="flex items-center justify-between mb-8 px-4"> | |
| <div> | |
| <div className="flex items-center space-x-2 text-blue-600 mb-1"> | |
| <Sparkles size={16} className="fill-current" /> | |
| <span className="text-[10px] font-black uppercase tracking-widest">AI Matching Engine</span> | |
| </div> | |
| <h2 className="text-3xl font-black text-gray-900">Recommended for You</h2> | |
| </div> | |
| <Link to="/search" className="text-blue-600 font-bold text-sm hover:underline">View all experts</Link> | |
| </div> | |
| <div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-8 px-4"> | |
| {recs.map((pg) => ( | |
| <Link | |
| key={pg._id} | |
| to={`/photographer/${pg._id}`} | |
| className="group bg-white rounded-[2.5rem] p-4 shadow-xl shadow-gray-200/50 hover:shadow-2xl hover:-translate-y-2 transition-all duration-500 border border-gray-100 flex flex-col" | |
| > | |
| <div className="relative h-64 w-full rounded-[2rem] overflow-hidden mb-6 shadow-inner"> | |
| <img src={pg.profilePicture} className="w-full h-full object-cover group-hover:scale-110 transition-transform duration-700" alt="" /> | |
| <div className="absolute top-4 right-4 bg-white/90 backdrop-blur-md px-4 py-2 rounded-2xl shadow-xl flex items-center space-x-1"> | |
| <TrendingUp size={14} className="text-green-500" /> | |
| <span className="text-[10px] font-black uppercase tracking-widest text-gray-900">{Math.round(pg.recommendationScore)}% Match</span> | |
| </div> | |
| </div> | |
| <div className="px-2 pb-2"> | |
| <div className="flex justify-between items-start mb-2"> | |
| <div> | |
| <h3 className="text-xl font-black text-gray-900 leading-tight">{pg.firstName} {pg.lastName}</h3> | |
| <p className="text-[10px] text-blue-600 font-black uppercase tracking-widest mt-1">{pg.specialty}</p> | |
| </div> | |
| <div className="flex items-center bg-amber-50 text-amber-600 px-3 py-1 rounded-xl shadow-sm border border-amber-100"> | |
| <Star size={12} className="fill-current mr-1" /> | |
| <span className="text-xs font-black">{pg.rating?.toFixed(1) || 4.9}</span> | |
| </div> | |
| </div> | |
| <p className="text-gray-400 text-xs font-bold uppercase tracking-widest mb-6 flex items-center"> | |
| <MapPin size={12} className="mr-1" /> {pg.address?.split(',')[0] || 'Local'} | |
| </p> | |
| <div className="flex items-center justify-between border-t border-gray-50 pt-5 mt-auto"> | |
| <div> | |
| <span className="text-[10px] text-gray-400 font-black uppercase block tracking-tighter">Starting at</span> | |
| <span className="text-2xl font-black text-gray-900">${pg.price}<span className="text-xs text-gray-400 font-bold">/hr</span></span> | |
| </div> | |
| <div className="bg-gray-900 text-white px-6 py-3 rounded-2xl font-black text-[10px] uppercase tracking-widest group-hover:bg-blue-600 transition shadow-xl"> | |
| Book Now | |
| </div> | |
| </div> | |
| </div> | |
| </Link> | |
| ))} | |
| </div> | |
| </section> | |
| ); | |
| }; | |
| export default Recommendations; | |