Spaces:
Build error
Build error
Upload src/app/search/page.tsx
Browse files- src/app/search/page.tsx +57 -29
src/app/search/page.tsx
CHANGED
|
@@ -1,34 +1,21 @@
|
|
| 1 |
'use client'
|
| 2 |
-
import { useState } from 'react'
|
|
|
|
|
|
|
| 3 |
|
| 4 |
-
const
|
| 5 |
-
const dists = ['All Districts','Colombo 1-7','Colombo 8-15','Kandy','Galle','Negombo','Battaramulla']
|
| 6 |
-
const sorts = ['Relevance','Rating','Price: Low–High','Price: High–Low']
|
| 7 |
-
const vendors = [
|
| 8 |
-
{ id:'1', name:'The Grand Atrium', cat:'Venues', dist:'Colombo 7', price:350000, rating:4.9, rev:127, emoji:'🏛️' },
|
| 9 |
-
{ id:'2', name:'Lens & Light Studio', cat:'Photographers', dist:'Colombo 3', price:150000, rating:4.8, rev:94, emoji:'📸' },
|
| 10 |
-
{ id:'3', name:'Bloom & Petal', cat:'Florists', dist:'Colombo 5', price:80000, rating:4.9, rev:68, emoji:'💐' },
|
| 11 |
-
{ id:'4', name:'Silver Spoon Catering', cat:'Catering', dist:'Colombo 4', price:250000, rating:4.7, rev:112, emoji:'🍽️' },
|
| 12 |
-
{ id:'5', name:'Melody Makers', cat:'Music & DJ', dist:'Colombo 6', price:75000, rating:4.6, rev:45, emoji:'🎵' },
|
| 13 |
-
{ id:'6', name:'Elegance Bridal', cat:'Bridal Wear', dist:'Colombo 7', price:120000, rating:4.8, rev:56, emoji:'👗' },
|
| 14 |
-
{ id:'7', name:'Dream Decor', cat:'Decorators', dist:'Kandy', price:95000, rating:4.7, rev:72, emoji:'✨' },
|
| 15 |
-
{ id:'8', name:'Sunset Gardens', cat:'Venues', dist:'Galle', price:400000, rating:4.9, rev:88, emoji:'🌅' },
|
| 16 |
-
]
|
| 17 |
|
| 18 |
export default function SearchPage() {
|
| 19 |
const [cat, setCat] = useState('All')
|
| 20 |
const [dist, setDist] = useState('All Districts')
|
| 21 |
const [sort, setSort] = useState('Relevance')
|
| 22 |
const [q, setQ] = useState('')
|
| 23 |
-
const
|
| 24 |
|
| 25 |
-
const
|
| 26 |
-
|
| 27 |
-
|
| 28 |
-
|
| 29 |
-
if (q && !v.name.toLowerCase().includes(q.toLowerCase())) return false
|
| 30 |
-
return true
|
| 31 |
-
})
|
| 32 |
|
| 33 |
return (
|
| 34 |
<div className="min-h-screen bg-ivory-50">
|
|
@@ -36,27 +23,68 @@ export default function SearchPage() {
|
|
| 36 |
<div className="wedding-section">
|
| 37 |
<h1 className="font-heading text-3xl font-semibold text-charcoal-700 mb-4">Explore Vendors</h1>
|
| 38 |
<div className="flex flex-wrap gap-3">
|
| 39 |
-
<input value={q} onChange={e=>setQ(e.target.value)} placeholder="Search vendors..." className="wedding-input max-w-md flex-1" />
|
| 40 |
-
<select value={sort} onChange={e=>setSort(e.target.value)} className="wedding-input w-auto">
|
|
|
|
|
|
|
| 41 |
</div>
|
| 42 |
</div>
|
| 43 |
</div>
|
|
|
|
| 44 |
<div className="wedding-section py-8">
|
| 45 |
<div className="flex gap-8">
|
| 46 |
<aside className="hidden lg:block w-56 shrink-0">
|
| 47 |
<div className="wedding-card p-5 space-y-6 sticky top-24">
|
| 48 |
<div>
|
| 49 |
<h3 className="font-heading text-sm font-semibold text-charcoal-700 mb-3">Category</h3>
|
| 50 |
-
<div className="space-y-1.5">
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 51 |
</div>
|
| 52 |
-
<div><h3 className="font-heading text-sm font-semibold text-charcoal-700 mb-3">District</h3><select value={dist} onChange={e=>setDist(e.target.value)} className="wedding-input text-sm">{dists.map(d=><option key={d}>{d}</option>)}</select></div>
|
| 53 |
-
{shortlist.length>0 && <div className="pt-4 border-t border-ivory-200"><a href="/shortlist" className="wedding-btn-gold w-full text-sm">Compare ({shortlist.length})</a></div>}
|
| 54 |
</div>
|
| 55 |
</aside>
|
|
|
|
| 56 |
<div className="flex-1">
|
| 57 |
-
<p className="text-sm text-charcoal-400 mb-4">{
|
| 58 |
<div className="grid grid-cols-1 sm:grid-cols-2 xl:grid-cols-3 gap-5">
|
| 59 |
-
{
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 60 |
</div>
|
| 61 |
</div>
|
| 62 |
</div>
|
|
|
|
| 1 |
'use client'
|
| 2 |
+
import { useState, useMemo } from 'react'
|
| 3 |
+
import { categories, districts, searchVendors, sortVendors } from '@/lib/vendors'
|
| 4 |
+
import { useApp } from '@/lib/app-context'
|
| 5 |
|
| 6 |
+
const sorts = ['Relevance', 'Rating', 'Price: Low–High', 'Price: High–Low', 'Most Reviewed']
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 7 |
|
| 8 |
export default function SearchPage() {
|
| 9 |
const [cat, setCat] = useState('All')
|
| 10 |
const [dist, setDist] = useState('All Districts')
|
| 11 |
const [sort, setSort] = useState('Relevance')
|
| 12 |
const [q, setQ] = useState('')
|
| 13 |
+
const { toggleShortlist, isShortlisted } = useApp()
|
| 14 |
|
| 15 |
+
const results = useMemo(() => {
|
| 16 |
+
const filtered = searchVendors(q, cat, dist)
|
| 17 |
+
return sortVendors(filtered, sort)
|
| 18 |
+
}, [q, cat, dist, sort])
|
|
|
|
|
|
|
|
|
|
| 19 |
|
| 20 |
return (
|
| 21 |
<div className="min-h-screen bg-ivory-50">
|
|
|
|
| 23 |
<div className="wedding-section">
|
| 24 |
<h1 className="font-heading text-3xl font-semibold text-charcoal-700 mb-4">Explore Vendors</h1>
|
| 25 |
<div className="flex flex-wrap gap-3">
|
| 26 |
+
<input value={q} onChange={e => setQ(e.target.value)} placeholder="Search vendors..." className="wedding-input max-w-md flex-1" />
|
| 27 |
+
<select value={sort} onChange={e => setSort(e.target.value)} className="wedding-input w-auto">
|
| 28 |
+
{sorts.map(s => <option key={s}>{s}</option>)}
|
| 29 |
+
</select>
|
| 30 |
</div>
|
| 31 |
</div>
|
| 32 |
</div>
|
| 33 |
+
|
| 34 |
<div className="wedding-section py-8">
|
| 35 |
<div className="flex gap-8">
|
| 36 |
<aside className="hidden lg:block w-56 shrink-0">
|
| 37 |
<div className="wedding-card p-5 space-y-6 sticky top-24">
|
| 38 |
<div>
|
| 39 |
<h3 className="font-heading text-sm font-semibold text-charcoal-700 mb-3">Category</h3>
|
| 40 |
+
<div className="space-y-1.5">
|
| 41 |
+
{categories.map(c => (
|
| 42 |
+
<button key={c} onClick={() => setCat(c)}
|
| 43 |
+
className={`block text-sm w-full text-left px-3 py-1.5 rounded-lg ${cat===c?'bg-charcoal-700 text-ivory-50':'text-charcoal-400 hover:bg-ivory-100'}`}>
|
| 44 |
+
{c}
|
| 45 |
+
</button>
|
| 46 |
+
))}
|
| 47 |
+
</div>
|
| 48 |
+
</div>
|
| 49 |
+
<div>
|
| 50 |
+
<h3 className="font-heading text-sm font-semibold text-charcoal-700 mb-3">District</h3>
|
| 51 |
+
<select value={dist} onChange={e => setDist(e.target.value)} className="wedding-input text-sm">
|
| 52 |
+
{districts.map(d => <option key={d}>{d}</option>)}
|
| 53 |
+
</select>
|
| 54 |
</div>
|
|
|
|
|
|
|
| 55 |
</div>
|
| 56 |
</aside>
|
| 57 |
+
|
| 58 |
<div className="flex-1">
|
| 59 |
+
<p className="text-sm text-charcoal-400 mb-4">{results.length} vendor{results.length!==1?'s':''} found</p>
|
| 60 |
<div className="grid grid-cols-1 sm:grid-cols-2 xl:grid-cols-3 gap-5">
|
| 61 |
+
{results.map(v => {
|
| 62 |
+
const shortlisted = isShortlisted(v.id)
|
| 63 |
+
return (
|
| 64 |
+
<a key={v.id} href={`/vendor/${v.id}`} className="wedding-card-hover overflow-hidden group">
|
| 65 |
+
<div className="h-40 bg-gradient-to-br from-ivory-200 to-rose-50 flex items-center justify-center text-5xl">{v.image}</div>
|
| 66 |
+
<div className="p-4">
|
| 67 |
+
<div className="flex items-center justify-between mb-2">
|
| 68 |
+
<span className="text-xs text-charcoal-300">{v.category}</span>
|
| 69 |
+
<button onClick={e => { e.preventDefault(); toggleShortlist(v.id) }}
|
| 70 |
+
className={`text-lg transition-colors ${shortlisted?'text-rose-400':'text-charcoal-200 hover:text-rose-400'}`}>
|
| 71 |
+
{shortlisted ? '♥' : '♡'}
|
| 72 |
+
</button>
|
| 73 |
+
</div>
|
| 74 |
+
<h3 className="font-heading text-base font-semibold text-charcoal-700 mb-1">{v.name}</h3>
|
| 75 |
+
<p className="text-xs text-charcoal-400 mb-3">{v.district}</p>
|
| 76 |
+
<div className="flex items-center justify-between">
|
| 77 |
+
<div className="flex items-center gap-1">
|
| 78 |
+
<span className="text-gold-400 text-sm">★</span>
|
| 79 |
+
<span className="text-sm font-medium text-charcoal-600">{v.rating}</span>
|
| 80 |
+
<span className="text-xs text-charcoal-300">({v.reviews})</span>
|
| 81 |
+
</div>
|
| 82 |
+
<span className="text-sm font-semibold text-charcoal-700">₨{(v.priceMin/1000).toFixed(0)}K+</span>
|
| 83 |
+
</div>
|
| 84 |
+
</div>
|
| 85 |
+
</a>
|
| 86 |
+
)
|
| 87 |
+
})}
|
| 88 |
</div>
|
| 89 |
</div>
|
| 90 |
</div>
|