imeshuek commited on
Commit
bb85bc6
·
verified ·
1 Parent(s): cc945be

Upload src/app/search/page.tsx

Browse files
Files changed (1) hide show
  1. 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 cats = ['All','Venues','Photographers','Florists','Catering','Music & DJ','Bridal Wear','Decorators']
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 [shortlist, setShortlist] = useState<string[]>([])
24
 
25
- const toggle = (id:string) => setShortlist(p => p.includes(id) ? p.filter(x=>x!==id) : [...p,id])
26
- const filtered = vendors.filter(v => {
27
- if (cat !== 'All' && v.cat !== cat) return false
28
- if (dist !== 'All Districts' && v.dist !== dist) return false
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">{sorts.map(s=><option key={s}>{s}</option>)}</select>
 
 
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">{cats.map(c=><button key={c} onClick={()=>setCat(c)} 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')}>{c}</button>)}</div>
 
 
 
 
 
 
 
 
 
 
 
 
 
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">{filtered.length} vendors found</p>
58
  <div className="grid grid-cols-1 sm:grid-cols-2 xl:grid-cols-3 gap-5">
59
- {filtered.map(v=><a key={v.id} href={"/vendor/"+v.id} className="wedding-card-hover overflow-hidden group"><div className="h-40 bg-gradient-to-br from-ivory-200 to-rose-50 flex items-center justify-center text-5xl">{v.emoji}</div><div className="p-4"><div className="flex items-center justify-between mb-2"><span className="text-xs text-charcoal-300">{v.cat}</span><button onClick={e=>{e.preventDefault();toggle(v.id)}} className={"text-lg "+(shortlist.includes(v.id)?'text-rose-400':'text-charcoal-200 hover:text-rose-400')}>{shortlist.includes(v.id)?'♥':'♡'}</button></div><h3 className="font-heading text-base font-semibold text-charcoal-700 mb-1">{v.name}</h3><p className="text-xs text-charcoal-400 mb-3">{v.dist}</p><div className="flex items-center justify-between"><div className="flex items-center gap-1"><span className="text-gold-400 text-sm">★</span><span className="text-sm font-medium text-charcoal-600">{v.rating}</span><span className="text-xs text-charcoal-300">({v.rev})</span></div><span className="text-sm font-semibold text-charcoal-700">₨{v.price.toLocaleString()}</span></div></div></a>)}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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>